The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: c692180b9ffdd70f50d08b0b7343969c68d8b0b4 $
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 PAIR_ALLOCED(vendor);
207
208 append = true;
209 }
210 }
211
213
214 /*
215 * Then, do the dirty job of creating attributes.
216 */
217 for (i = 0; i < arg_cnt; i++) {
218 uint8_t const *value, *name_end, *arg_end;
219 fr_dict_attr_t const *da;
220 fr_pair_list_t *dst;
221 uint8_t buffer[256];
222
223 if (argv[i] > (end - p)) return -1;
224
225 if (argv[i] < 2) goto next; /* skip malformed */
226
227 memcpy(buffer, p, argv[i]);
228 buffer[argv[i]] = '\0';
229
230 arg_end = buffer + argv[i];
231
232 for (value = buffer, name_end = NULL; value < arg_end; value++) {
233 /*
234 * RFC 8907 Section 3.7 says control
235 * characters MUST be excluded.
236 */
237 if (*value < ' ') goto next;
238
239 if ((*value == '=') || (*value == '*')) {
240 name_end = value;
241 buffer[value - buffer] = '\0';
242 value++;
243 break;
244 }
245 }
246
247 /*
248 * Skip fields which aren't in "name=value" or "name*value" format.
249 */
250 if (!name_end) goto next;
251
252 /*
253 * Prefer to decode from the attribute root, first.
254 */
255 da = fr_dict_attr_by_name(NULL, root, (char *) buffer);
256 if (da) {
257 vp = fr_pair_afrom_da(ctx, da);
258 if (!vp) goto oom;
260
261 dst = out;
262 goto decode;
263 }
264
265 /*
266 * If the attribute isn't in the main dictionary,
267 * maybe it's in the vendor dictionary?
268 */
269 if (vendor) {
270 da = fr_dict_attr_by_name(NULL, parent, (char *) buffer);
271 if (!da) goto raw;
272
273 vp = fr_pair_afrom_da(vendor, da);
274 if (!vp) goto oom;
276
277 dst = &vendor->vp_group;
278
279 decode:
280 /*
281 * If it's OCTETS or STRING type, then just copy the value verbatim, as the
282 * contents are (should be?) binary-safe. But if it's zero length, then don't need to
283 * copy anything.
284 *
285 * Note that we copy things manually here because
286 * we don't want the OCTETS type to be parsed as
287 * hex. And, we don't want the string type to be
288 * unescaped.
289 */
290 if (da->type == FR_TYPE_OCTETS) {
291 if ((arg_end > value) &&
292 (fr_pair_value_memdup(vp, value, arg_end - value, true) < 0)) {
293 goto fail;
294 }
295
296 } else if (da->type == FR_TYPE_STRING) {
297 if ((arg_end > value) &&
298 (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
299 goto fail;
300 }
301
302 } else if (arg_end == value) {
303 /*
304 * Any other leaf type MUST have non-zero contents.
305 */
307 goto raw;
308
309 } else {
310 /*
311 * Parse the string, and try to convert it to the
312 * underlying data type. If it can't be
313 * converted as a data type, just convert it as
314 * Argument-List.
315 *
316 * And if that fails, just ignore it completely.
317 */
318 if (fr_pair_value_from_str(vp, (char const *) value, arg_end - value, NULL, true) < 0) {
320 goto raw;
321 }
322
323 /*
324 * Else it parsed fine, append it to the output vendor list.
325 */
326 }
327
328 fr_pair_append(dst, vp);
329
330 } else {
331 raw:
333 if (!vp) {
334 oom:
335 fr_strerror_const("Out of Memory");
336 fail:
337 if (append) {
338 talloc_free(vendor);
339 } else {
341 }
342 return -1;
343 }
345
346 value = p;
347 arg_end = p + argv[i];
348
349 if ((arg_end > value) &&
350 (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
351 goto fail;
352 }
353
355 }
356
357 next:
358 p += argv[i];
359 }
360
361 if (append) {
362 if (fr_pair_list_num_elements(&vendor->vp_group) > 0) {
363 fr_pair_append(out, vendor);
364 } else {
365 talloc_free(vendor);
366 }
367 }
368
369 return 0;
370}
371
372/**
373 * Decode a TACACS+ field.
374 */
375static int tacacs_decode_field(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da,
376 uint8_t const **field_data, uint16_t field_len, uint8_t const *end)
377{
378 uint8_t const *p = *field_data;
379 fr_pair_t *vp;
380
381 if (field_len > (end - p)) {
382 fr_strerror_printf("'%s' length %u overflows the remaining data (%zu) in the packet",
383 da->name, field_len, (size_t) (end - p));
384 return -1;
385 }
386
387 vp = fr_pair_afrom_da(ctx, da);
388 if (!vp) {
389 fr_strerror_const("Out of Memory");
390 return -1;
391 }
393
394 if (field_len) {
395 if (da->type == FR_TYPE_STRING) {
396 fr_pair_value_bstrndup(vp, (char const *)p, field_len, true);
397 } else if (da->type == FR_TYPE_OCTETS) {
398 fr_pair_value_memdup(vp, p, field_len, true);
399 } else {
400 fr_assert(0);
401 }
402 p += field_len;
403 *field_data = p;
404 }
405
407
408 return 0;
409}
410
411/**
412 * Decode a TACACS+ packet
413 */
415 uint8_t const *buffer, size_t buffer_len,
416 const uint8_t *original, char const * const secret, size_t secret_len, int *code)
417{
418 fr_tacacs_packet_t const *pkt;
419 fr_pair_t *vp;
420 size_t data_len;
421 uint8_t const *p, *body, *argv, *attrs, *end;
422 uint8_t *decrypted = NULL;
423
424 /*
425 * 3.4. The TACACS+ Packet Header
426 *
427 * 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
428 * +----------------+----------------+----------------+----------------+
429 * |major | minor | | | |
430 * |version| version| type | seq_no | flags |
431 * +----------------+----------------+----------------+----------------+
432 * | |
433 * | session_id |
434 * +----------------+----------------+----------------+----------------+
435 * | |
436 * | length |
437 * +----------------+----------------+----------------+----------------+
438 */
439 pkt = (fr_tacacs_packet_t const *) buffer;
440
441 /*
442 * p miscellaneous pointer for decoding things
443 * body points to just past the (randomly sized) per-packet header,
444 * where the various user / server messages are.
445 * sometimes this is after "argv".
446 * argv points to the array of argv[i] length entries
447 * attrs points to the attributes we need to decode as "foo=bar".
448 */
449 argv = attrs = NULL;
450 end = buffer + buffer_len;
451
452 /*
453 * Check that we have a full TACACS+ header before
454 * decoding anything.
455 */
456 if (buffer_len < sizeof(pkt->hdr)) {
457 fr_strerror_printf("Packet is too small (%zu < 12) to be TACACS+.", buffer_len);
458 return -1;
459 }
460
461 /*
462 * TACACS major / minor version MUST be 12.0 or 12.1
463 */
464 if (!(pkt->hdr.ver.major == 12 && (pkt->hdr.ver.minor == 0 || pkt->hdr.ver.minor == 1))) {
465 fr_strerror_printf("Unsupported TACACS+ version %d.%d (%02x)", pkt->hdr.ver.major, pkt->hdr.ver.minor, buffer[0]);
466 return -1;
467 }
468
469 /*
470 * There's no reason to accept 64K TACACS+ packets.
471 *
472 * In any case, the largest possible packet has the
473 * header, plus 2 16-bit fields, plus 255 8-bit fields,
474 * which is a bit under 2^18.
475 */
476 if ((buffer[8] != 0) || (buffer[9] != 0)) {
477 fr_strerror_const("Packet is too large. Our limit is 64K");
478 return -1;
479 }
480
481 /*
482 * As a stream protocol, the TACACS+ packet MUST fit
483 * exactly into however many bytes we read.
484 */
485 if ((buffer + sizeof(pkt->hdr) + ntohl(pkt->hdr.length)) != end) {
486 fr_strerror_const("Packet does not exactly fill buffer");
487 return -1;
488 }
489
490 /*
491 * There are only 3 types of packets which are supported.
492 */
493 if (!((pkt->hdr.type == FR_TAC_PLUS_AUTHEN) ||
494 (pkt->hdr.type == FR_TAC_PLUS_AUTHOR) ||
495 (pkt->hdr.type == FR_TAC_PLUS_ACCT))) {
496 fr_strerror_printf("Unknown packet type %d", pkt->hdr.type);
497 return -1;
498 }
499
500 /*
501 * Check that the session IDs are correct.
502 */
503 if (original && (memcmp(original + 4, buffer + 4, 4) != 0)) {
504 fr_strerror_printf("Session ID %08x does not match expected number %08x",
505 fr_nbo_to_uint32(buffer + 4), fr_nbo_to_uint32(original + 4));
506 return -1;
507 }
508
509 if (!secret && packet_is_encrypted(pkt)) {
510 fr_strerror_const("Packet is encrypted, but there is no secret to decrypt it");
511 return -1;
512 }
513
514 /*
515 * Call the struct encoder to do the actual work.
516 */
517 if (fr_struct_from_network(ctx, out, attr_tacacs_packet, buffer, buffer_len, NULL, NULL, NULL) < 0) {
518 fr_strerror_printf_push("Failed decoding TACACS header");
519 return -1;
520 }
521
522 /*
523 * 3.6. Encryption
524 *
525 * If there's a secret, we always decrypt the packets.
526 */
527 if (secret && packet_is_encrypted(pkt)) {
528 size_t length;
529
530 if (!secret_len) {
531 fr_strerror_const("Packet should be encrypted, but the secret has zero length");
532 return -1;
533 }
534
535 length = ntohl(pkt->hdr.length);
536
537 /*
538 * We need that to decrypt the body content.
539 *
540 * @todo - use thread-local storage to avoid allocations?
541 */
542 decrypted = talloc_memdup(ctx, buffer, buffer_len);
543 if (!decrypted) {
544 fr_strerror_const("Out of Memory");
545 return -1;
546 }
547
548 pkt = (fr_tacacs_packet_t const *) decrypted;
549 end = decrypted + buffer_len;
550
551 if (fr_tacacs_body_xor(pkt, decrypted + sizeof(pkt->hdr), length, secret, secret_len) < 0) {
552 fail:
553 talloc_free(decrypted);
554 return -1;
555 }
556
557 decrypted[3] |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
558
559 FR_PROTO_HEX_DUMP(decrypted, buffer_len, "fr_tacacs_packet_t (unencrypted)");
560
561 buffer = decrypted;
562 }
563
564#ifndef NDEBUG
566#endif
567
568 if (code) {
570 if (*code < 0) goto fail;
571 }
572
573 switch (pkt->hdr.type) {
576 uint8_t want;
577 bool raw;
578 fr_dict_attr_t const *da, *challenge;
579
580 /**
581 * 4.1. The Authentication START Packet Body
582 *
583 * 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
584 * +----------------+----------------+----------------+----------------+
585 * | action | priv_lvl | authen_type | authen_service |
586 * +----------------+----------------+----------------+----------------+
587 * | user_len | port_len | rem_addr_len | data_len |
588 * +----------------+----------------+----------------+----------------+
589 * | user ...
590 * +----------------+----------------+----------------+----------------+
591 * | port ...
592 * +----------------+----------------+----------------+----------------+
593 * | rem_addr ...
594 * +----------------+----------------+----------------+----------------+
595 * | data...
596 * +----------------+----------------+----------------+----------------+
597 */
598 PACKET_HEADER_CHECK("Authentication-Start", pkt->authen_start);
599
600 data_len += p[4] + p[5] + p[6] + p[7];
601 if (data_len > (size_t) (end - p)) {
602 overflow:
603 if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) {
604 bad_secret:
605 fr_strerror_const("Invalid packet after decryption - is the secret key incorrect?");
606 goto fail;
607 }
608
609 fr_strerror_const("Data overflows the packet");
610 goto fail;
611 }
612 if (data_len < (size_t) (end - p)) {
613 underflow:
614 if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) goto bad_secret;
615
616 fr_strerror_const("Data underflows the packet");
617 goto fail;
618 }
619
621
622 /*
623 * Decode 4 octets of various flags.
624 */
625 DECODE_FIELD_UINT8(attr_tacacs_action, pkt->authen_start.action);
626 DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->authen_start.priv_lvl);
627 DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->authen_start.authen_type);
628 DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->authen_start.authen_service);
629
630 /*
631 * Decode 3 fields, based on their "length"
632 * user and rem_addr are optional - indicated by zero length
633 */
634 p = body;
635 if (pkt->authen_start.user_len > 0) DECODE_FIELD_STRING8(attr_tacacs_user_name,
636 pkt->authen_start.user_len);
637 DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->authen_start.port_len);
638 if (pkt->authen_start.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
639 pkt->authen_start.rem_addr_len);
640
641 /*
642 * Check the length on the various
643 * authentication types.
644 */
645 raw = false;
646 challenge = NULL;
647
648 switch (pkt->authen_start.authen_type) {
649 default:
650 raw = true;
651 want = pkt->authen_start.data_len;
652 da = attr_tacacs_data;
653 break;
654
655 case FR_AUTHENTICATION_TYPE_VALUE_PAP:
656 want = pkt->authen_start.data_len;
658 break;
659
660 case FR_AUTHENTICATION_TYPE_VALUE_CHAP:
661 want = 1 + 16; /* id + HOPEFULLY 8 octets of challenge + 16 hash */
663 challenge = attr_tacacs_chap_challenge;
664 break;
665
666 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
667 want = 1 + 49; /* id + HOPEFULLY 8 octets of challenge + 49 MS-CHAP stuff */
670 break;
671
672 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
673 want = 1 + 49; /* id + HOPEFULLY 16 octets of challenge + 49 MS-CHAP stuff */
676 break;
677 }
678
679 /*
680 * If we have enough data, decode it as
681 * the claimed authentication type.
682 *
683 * Otherwise, decode the entire field as an unknown
684 * attribute.
685 */
686 if (raw || (pkt->authen_start.data_len < want)) {
687 fr_dict_attr_t *da_unknown;
688
690 attr_tacacs_data->attr);
691 if (!da_unknown) goto fail;
692
693 want = pkt->authen_start.data_len;
694
695 DECODE_FIELD_STRING8(da_unknown, want);
696 talloc_free(da_unknown);
697
698 } else if (!challenge) {
699 DECODE_FIELD_STRING8(da, want);
700
701 } else if (pkt->authen_start.data_len == want) {
702 fr_strerror_printf("%s has zero length", challenge->name);
703 goto fail;
704
705 } else { /* 1 of ID + ??? of challenge + (want-1) of data */
706 uint8_t challenge_len = pkt->authen_start.data_len - want;
707 uint8_t hash[50];
708
709 /*
710 * Rework things to make sense.
711 * RFC 8079 says that MS-CHAP responses should follow RFC 2433 and 2759
712 * which have "Flags" at the end.
713 * RADIUS attributes expect "Flags" after the ID as per RFC 2548.
714 * Re-arrange to make things consistent.
715 */
716 hash[0] = p[0];
717 switch (pkt->authen_start.authen_type) {
718 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
719 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
720 hash[1] = p[want - 1];
721 memcpy(hash + 2, p + 1 + challenge_len, want - 2);
722 break;
723
724 default:
725 memcpy(hash + 1, p + 1 + challenge_len, want - 1);
726 break;
727 }
728
729 vp = fr_pair_afrom_da(ctx, da);
730 if (!vp) goto fail;
731
733
734 /*
735 * ID + hash
736 */
737 if (fr_pair_value_memdup(vp, hash, want, true) < 0) goto fail;
738
739 /*
740 * And then the challenge.
741 */
742 vp = fr_pair_afrom_da(ctx, challenge);
743 if (!vp) goto fail;
744
746
747 if (fr_pair_value_memdup(vp, p + 1, challenge_len, true) < 0) goto fail;
748
749 p += pkt->authen_start.data_len;
750 }
751
752 } else if (packet_is_authen_continue(pkt)) {
753 /*
754 * 4.3. The Authentication CONTINUE Packet Body
755 *
756 * This packet is sent from the client to the server following the receipt of
757 * a REPLY packet.
758 *
759 * 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
760 * +----------------+----------------+----------------+----------------+
761 * | user_msg len | data_len |
762 * +----------------+----------------+----------------+----------------+
763 * | flags | user_msg ...
764 * +----------------+----------------+----------------+----------------+
765 * | data ...
766 * +----------------+
767 */
768
769 /*
770 * Version 1 is ONLY used for PAP / CHAP
771 * / MS-CHAP start and reply packets.
772 */
773 if (pkt->hdr.ver.minor != 0) {
774 invalid_version:
775 fr_strerror_const("Invalid TACACS+ version");
776 goto fail;
777 }
778
779 PACKET_HEADER_CHECK("Authentication-Continue", pkt->authen_cont);
780 data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
781 if (data_len > (size_t) (end - p)) goto overflow;
782 if (data_len < (size_t) (end - p)) goto underflow;
783
785
786 /*
787 * Decode 2 fields, based on their "length"
788 */
789 p = body;
790 DECODE_FIELD_STRING16(attr_tacacs_user_message, pkt->authen_cont.user_msg_len);
791 DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_cont.data_len);
792
793 /*
794 * And finally the flags.
795 */
797
798 } else if (packet_is_authen_reply(pkt)) {
799 /*
800 * 4.2. The Authentication REPLY Packet Body
801 *
802 * 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
803 * +----------------+----------------+----------------+----------------+
804 * | status | flags | server_msg_len |
805 * +----------------+----------------+----------------+----------------+
806 * | data_len | server_msg ...
807 * +----------------+----------------+----------------+----------------+
808 * | data ...
809 * +----------------+----------------+
810 */
811
812 /*
813 * We don't care about versions for replies.
814 * We just echo whatever was sent in the request.
815 */
816 PACKET_HEADER_CHECK("Authentication-Reply", pkt->authen_reply);
817 data_len += fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
818 if (data_len > (size_t) (end - p)) goto overflow;
819 if (data_len < (size_t) (end - p)) goto underflow;
820
822
823 DECODE_FIELD_UINT8(attr_tacacs_authentication_status, pkt->authen_reply.status);
825
826 /*
827 * Decode 2 fields, based on their "length"
828 */
829 p = body;
830 DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->authen_reply.server_msg_len);
831 DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_reply.data_len);
832
833 } else {
834 fr_strerror_const("Unknown authentication packet");
835 goto fail;
836 }
837 break;
838
840 if (packet_is_author_request(pkt)) {
841 /*
842 * 5.1. The Authorization REQUEST Packet Body
843 *
844 * 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
845 * +----------------+----------------+----------------+----------------+
846 * | authen_method | priv_lvl | authen_type | authen_service |
847 * +----------------+----------------+----------------+----------------+
848 * | user_len | port_len | rem_addr_len | arg_cnt |
849 * +----------------+----------------+----------------+----------------+
850 * | arg_1_len | arg_2_len | ... | arg_N_len |
851 * +----------------+----------------+----------------+----------------+
852 * | user ...
853 * +----------------+----------------+----------------+----------------+
854 * | port ...
855 * +----------------+----------------+----------------+----------------+
856 * | rem_addr ...
857 * +----------------+----------------+----------------+----------------+
858 * | arg_1 ...
859 * +----------------+----------------+----------------+----------------+
860 * | arg_2 ...
861 * +----------------+----------------+----------------+----------------+
862 * | ...
863 * +----------------+----------------+----------------+----------------+
864 * | arg_N ...
865 * +----------------+----------------+----------------+----------------+
866 */
867
868 if (pkt->hdr.ver.minor != 0) goto invalid_version;
869
870 PACKET_HEADER_CHECK("Authorization-Request", pkt->author_req);
871 data_len += p[4] + p[5] + p[6] + p[7];
872
873 ARG_COUNT_CHECK("Authorization-Request", pkt->author_req);
874
876
877 /*
878 * Decode 4 octets of various flags.
879 */
880 DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->author_req.authen_method);
881 DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->author_req.priv_lvl);
882 DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->author_req.authen_type);
883 DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->author_req.authen_service);
884
885 /*
886 * Decode 3 fields, based on their "length"
887 * rem_addr is optional - indicated by zero length
888 */
889 p = body;
890 DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->author_req.user_len);
891 DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->author_req.port_len);
892 if (pkt->author_req.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
893 pkt->author_req.rem_addr_len);
894
895 /*
896 * Decode 'arg_N' arguments (horrible format)
897 */
898 if (tacacs_decode_args(ctx, out, vendor,
899 pkt->author_req.arg_cnt, argv, attrs, end) < 0) goto fail;
900
901 } else if (packet_is_author_reply(pkt)) {
902 /*
903 * 5.2. The Authorization RESPONSE Packet Body
904 *
905 * 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
906 * +----------------+----------------+----------------+----------------+
907 * | status | arg_cnt | server_msg len |
908 * +----------------+----------------+----------------+----------------+
909 * + data_len | arg_1_len | arg_2_len |
910 * +----------------+----------------+----------------+----------------+
911 * | ... | arg_N_len | server_msg ...
912 * +----------------+----------------+----------------+----------------+
913 * | data ...
914 * +----------------+----------------+----------------+----------------+
915 * | arg_1 ...
916 * +----------------+----------------+----------------+----------------+
917 * | arg_2 ...
918 * +----------------+----------------+----------------+----------------+
919 * | ...
920 * +----------------+----------------+----------------+----------------+
921 * | arg_N ...
922 * +----------------+----------------+----------------+----------------+
923 */
924
925 /*
926 * We don't care about versions for replies.
927 * We just echo whatever was sent in the request.
928 */
929
930 PACKET_HEADER_CHECK("Authorization-Reply", pkt->author_reply);
931 data_len += p[1] + fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
932
933 ARG_COUNT_CHECK("Authorization-Reply", pkt->author_reply);
935
936 /*
937 * Decode 1 octets
938 */
939 DECODE_FIELD_UINT8(attr_tacacs_authorization_status, pkt->author_reply.status);
940
941 /*
942 * Decode 2 fields, based on their "length"
943 */
944 p = body;
945 DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->author_reply.server_msg_len);
946 DECODE_FIELD_STRING16(attr_tacacs_data, pkt->author_reply.data_len);
947
948 /*
949 * Decode 'arg_N' arguments (horrible format)
950 */
951 if (tacacs_decode_args(ctx, out, vendor,
952 pkt->author_reply.arg_cnt, argv, attrs, end) < 0) goto fail;
953
954 } else {
955 fr_strerror_const("Unknown authorization packet");
956 goto fail;
957 }
958 break;
959
960 case FR_TAC_PLUS_ACCT:
961 if (packet_is_acct_request(pkt)) {
962 /**
963 * 6.1. The Account REQUEST Packet Body
964 *
965 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
966 * +----------------+----------------+----------------+----------------+
967 * | flags | authen_method | priv_lvl | authen_type |
968 * +----------------+----------------+----------------+----------------+
969 * | authen_service | user_len | port_len | rem_addr_len |
970 * +----------------+----------------+----------------+----------------+
971 * | arg_cnt | arg_1_len | arg_2_len | ... |
972 * +----------------+----------------+----------------+----------------+
973 * | arg_N_len | user ...
974 * +----------------+----------------+----------------+----------------+
975 * | port ...
976 * +----------------+----------------+----------------+----------------+
977 * | rem_addr ...
978 * +----------------+----------------+----------------+----------------+
979 * | arg_1 ...
980 * +----------------+----------------+----------------+----------------+
981 * | arg_2 ...
982 * +----------------+----------------+----------------+----------------+
983 * | ...
984 * +----------------+----------------+----------------+----------------+
985 * | arg_N ...
986 * +----------------+----------------+----------------+----------------+
987 */
988
989 if (pkt->hdr.ver.minor != 0) goto invalid_version;
990
991 PACKET_HEADER_CHECK("Accounting-Request", pkt->acct_req);
992 data_len += p[5] + p[6] + p[7] + p[8];
993 if (data_len > (size_t) (end - p)) goto overflow;
994 /* can't check for underflow, as we have argv[argc] */
995
996 ARG_COUNT_CHECK("Accounting-Request", pkt->acct_req);
997
999
1000 /*
1001 * Decode 5 octets of various flags.
1002 */
1004 DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->acct_req.authen_method);
1005 DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->acct_req.priv_lvl);
1006 DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->acct_req.authen_type);
1007 DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->acct_req.authen_service);
1008
1009 /*
1010 * Decode 3 fields, based on their "length"
1011 */
1012 p = body;
1013 DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->acct_req.user_len);
1014 DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->acct_req.port_len);
1015 DECODE_FIELD_STRING8(attr_tacacs_remote_address, pkt->acct_req.rem_addr_len);
1016
1017 /*
1018 * Decode 'arg_N' arguments (horrible format)
1019 */
1020 if (tacacs_decode_args(ctx, out, vendor,
1021 pkt->acct_req.arg_cnt, argv, attrs, end) < 0) goto fail;
1022
1023 } else if (packet_is_acct_reply(pkt)) {
1024 /**
1025 * 6.2. The Accounting REPLY Packet Body
1026 *
1027 * 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
1028 * +----------------+----------------+----------------+----------------+
1029 * | server_msg len | data_len |
1030 * +----------------+----------------+----------------+----------------+
1031 * | status | server_msg ...
1032 * +----------------+----------------+----------------+----------------+
1033 * | data ...
1034 * +----------------+
1035 */
1036
1037 /*
1038 * We don't care about versions for replies.
1039 * We just echo whatever was sent in the request.
1040 */
1041
1042 PACKET_HEADER_CHECK("Accounting-Reply", pkt->acct_reply);
1043 data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
1044 if (data_len > (size_t) (end - p)) goto overflow;
1045 if (data_len < (size_t) (end - p)) goto underflow;
1046
1047 p = BODY(acct_reply);
1049
1050 /*
1051 * Decode 2 fields, based on their "length"
1052 */
1053 DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->acct_reply.server_msg_len);
1054 DECODE_FIELD_STRING16(attr_tacacs_data, pkt->acct_reply.data_len);
1055
1056 /* Decode 1 octet */
1057 DECODE_FIELD_UINT8(attr_tacacs_accounting_status, pkt->acct_reply.status);
1058 } else {
1059 fr_strerror_const("Unknown accounting packet");
1060 goto fail;
1061 }
1062 break;
1063 default:
1064 fr_strerror_printf("decode: Unsupported packet type %d", pkt->hdr.type);
1065 goto fail;
1066 }
1067
1068 talloc_free(decrypted);
1069 return buffer_len;
1070}
1071
1072/*
1073 * Test points for protocol decode
1074 */
1075static 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)
1076{
1077 fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1078 fr_dict_attr_t const *dv;
1079
1080 dv = fr_dict_attr_by_name(NULL, fr_dict_root(dict_tacacs), "Test");
1081 fr_assert(!dv || (dv->type == FR_TYPE_VENDOR));
1082
1083 return fr_tacacs_decode(ctx, out, dv, data, data_len, NULL, test_ctx->secret,
1084 test_ctx->secret ? talloc_strlen(test_ctx->secret) : 0, NULL);
1085}
1086
1088{
1089 talloc_const_free(test_ctx->secret);
1090
1092
1093 return 0;
1094}
1095
1096static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
1098{
1099 fr_tacacs_ctx_t *test_ctx;
1100
1101 if (fr_tacacs_global_init() < 0) return -1;
1102
1103 test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1104 if (!test_ctx) return -1;
1105
1106 test_ctx->secret = talloc_strdup(test_ctx, "testing123");
1107 test_ctx->root = fr_dict_root(dict_tacacs);
1108 talloc_set_destructor(test_ctx, _encode_test_ctx);
1109
1110 *out = test_ctx;
1111
1112 return 0;
1113}
1114
static int const char char buffer[256]
Definition acutest.h:576
#define NDEBUG_UNUSED
Definition build.h:395
#define UNUSED
Definition build.h:384
fr_dict_attr_t const * root_da
Definition common.c:32
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:3603
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:635
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2720
Test enumeration values.
Definition dict_test.h:92
talloc_free(hp)
int fr_debug_lvl
Definition log.c:41
fr_log_t default_log
Definition log.c:308
@ L_DBG_LVL_4
4th highest priority debug messages (-xxxx | -Xxx).
Definition log.h:70
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:1198
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:2894
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2549
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:708
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:1298
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:291
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:2744
static fr_dict_attr_t const * attr_tacacs_authentication_flags
Definition base.c:53
static fr_dict_attr_t const * attr_tacacs_user_message
Definition base.c:70
static fr_dict_attr_t const * attr_tacacs_server_message
Definition base.c:66
static fr_dict_attr_t const * attr_tacacs_privilege_level
Definition base.c:64
static fr_dict_attr_t const * attr_tacacs_authentication_type
Definition base.c:54
static fr_dict_attr_t const * attr_tacacs_accounting_status
Definition base.c:59
static fr_dict_attr_t const * attr_tacacs_authentication_service
Definition base.c:55
static fr_dict_attr_t const * attr_tacacs_authentication_status
Definition base.c:56
static fr_dict_attr_t const * attr_tacacs_client_port
Definition base.c:62
static fr_dict_attr_t const * attr_tacacs_remote_address
Definition base.c:65
static fr_dict_attr_t const * attr_tacacs_action
Definition base.c:51
static fr_dict_attr_t const * attr_tacacs_authorization_status
Definition base.c:58
static fr_dict_attr_t const * attr_tacacs_accounting_flags
Definition base.c:60
static fr_dict_attr_t const * attr_tacacs_data
Definition base.c:63
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, UNUSED fr_dict_attr_t const *root_da)
Definition decode.c:102
HIDDEN fr_dict_attr_t const * attr_tacacs_argument_list
Definition base.c:55
HIDDEN fr_dict_attr_t const * attr_tacacs_chap_password
Definition base.c:74
HIDDEN fr_dict_attr_t const * attr_tacacs_mschap_challenge
Definition base.c:78
HIDDEN fr_dict_attr_t const * attr_tacacs_packet
Definition base.c:60
HIDDEN fr_dict_attr_t const * attr_tacacs_mschap2_response
Definition base.c:77
HIDDEN fr_dict_attr_t const * attr_tacacs_authentication_continue_flags
Definition base.c:49
HIDDEN fr_dict_attr_t const * attr_tacacs_user_password
Definition base.c:73
HIDDEN fr_dict_attr_t const * attr_tacacs_packet_body_type
Definition base.c:61
HIDDEN fr_dict_attr_t const * attr_tacacs_chap_challenge
Definition base.c:75
HIDDEN fr_dict_attr_t const * attr_tacacs_mschap_response
Definition base.c:76
HIDDEN fr_dict_attr_t const * attr_tacacs_authentication_method
Definition base.c:50
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:148
void fr_tacacs_global_free(void)
Definition base.c:610
int fr_tacacs_global_init(void)
Definition base.c:586
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
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:414
#define DECODE_FIELD_UINT8(_da, _field)
Definition decode.c:159
fr_test_point_proto_decode_t tacacs_tp_decode_proto
Definition decode.c:1116
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:1075
#define BODY(_x)
Definition decode.c:176
static int _encode_test_ctx(fr_tacacs_ctx_t *test_ctx)
Definition decode.c:1087
#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:375
#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:37
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:32
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:288
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:68
Entry point for protocol decoders.
Definition test_point.h:67
#define PAIR_ALLOCED(_x)
Definition pair.h:213
#define FR_PAIR_APPEND
Definition pair.h:214
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:860
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition proto.h:42
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1367
static fr_sbuff_err_t char ** out
Definition value.h:1062