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