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: 62edca095bea5e3e76166b5cee003ba74a9bc97e $
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 PAIR_ALLOCED(vp); \
163 vp->vp_uint8 = _field; \
164 fr_pair_append(out, vp); \
165} while (0)
166
167#define DECODE_FIELD_STRING8(_da, _field) do { \
168 if (tacacs_decode_field(ctx, out, _da, &p, \
169 _field, end) < 0) goto fail; \
170} while (0)
171
172#define DECODE_FIELD_STRING16(_da, _field) do { \
173 if (tacacs_decode_field(ctx, out, _da, &p, \
174 ntohs(_field), end) < 0) goto fail; \
175} while (0)
176
177#define BODY(_x) (((uint8_t const *) pkt) + sizeof(pkt->hdr) + sizeof(pkt->_x))
178
179/** Decode a TACACS+ 'arg_N' fields.
180 *
181 */
182static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent,
183 uint8_t arg_cnt, uint8_t const *argv, uint8_t const *attrs, NDEBUG_UNUSED uint8_t const *end)
184{
185 uint8_t i;
186 bool append = false;
187 uint8_t const *p = attrs;
188 fr_pair_t *vp;
189 fr_pair_t *vendor = NULL;
190 fr_dict_attr_t const *root;
191
192 /*
193 * No one? Just get out!
194 */
195 if (!arg_cnt) return 0;
196
197 /*
198 * Try to decode as nested attributes. If we can't, everything is
199 *
200 * Argument-List = "foo=bar"
201 */
202 if (parent) {
203 vendor = fr_pair_find_by_da(out, NULL, parent);
204 if (!vendor) {
205 vendor = fr_pair_afrom_da(ctx, parent);
206 if (!vendor) return -1;
207 PAIR_ALLOCED(vendor);
208
209 append = true;
210 }
211 }
212
214
215 /*
216 * Then, do the dirty job of creating attributes.
217 */
218 for (i = 0; i < arg_cnt; i++) {
219 uint8_t const *value, *name_end, *arg_end;
220 fr_dict_attr_t const *da;
221 fr_pair_list_t *dst;
222 uint8_t buffer[256];
223
224 fr_assert((p + argv[i]) <= end);
225
226 if (argv[i] < 2) goto next; /* skip malformed */
227
228 memcpy(buffer, p, argv[i]);
229 buffer[argv[i]] = '\0';
230
231 arg_end = buffer + argv[i];
232
233 for (value = buffer, name_end = NULL; value < arg_end; value++) {
234 /*
235 * RFC 8907 Section 3.7 says control
236 * characters MUST be excluded.
237 */
238 if (*value < ' ') goto next;
239
240 if ((*value == '=') || (*value == '*')) {
241 name_end = value;
242 buffer[value - buffer] = '\0';
243 value++;
244 break;
245 }
246 }
247
248 /*
249 * Skip fields which aren't in "name=value" or "name*value" format.
250 */
251 if (!name_end) goto next;
252
253 /*
254 * Prefer to decode from the attribute root, first.
255 */
256 da = fr_dict_attr_by_name(NULL, root, (char *) buffer);
257 if (da) {
258 vp = fr_pair_afrom_da(ctx, da);
259 if (!vp) goto oom;
261
262 dst = out;
263 goto decode;
264 }
265
266 /*
267 * If the attribute isn't in the main dictionary,
268 * maybe it's in the vendor dictionary?
269 */
270 if (vendor) {
271 da = fr_dict_attr_by_name(NULL, parent, (char *) buffer);
272 if (!da) goto raw;
273
274 vp = fr_pair_afrom_da(vendor, da);
275 if (!vp) goto oom;
277
278 dst = &vendor->vp_group;
279
280 decode:
281 /*
282 * If it's OCTETS or STRING type, then just copy the value verbatim, as the
283 * contents are (should be?) binary-safe. But if it's zero length, then don't need to
284 * copy anything.
285 *
286 * Note that we copy things manually here because
287 * we don't want the OCTETS type to be parsed as
288 * hex. And, we don't want the string type to be
289 * unescaped.
290 */
291 if (da->type == FR_TYPE_OCTETS) {
292 if ((arg_end > value) &&
293 (fr_pair_value_memdup(vp, value, arg_end - value, true) < 0)) {
294 goto fail;
295 }
296
297 } else if (da->type == FR_TYPE_STRING) {
298 if ((arg_end > value) &&
299 (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
300 goto fail;
301 }
302
303 } else if (arg_end == value) {
304 /*
305 * Any other leaf type MUST have non-zero contents.
306 */
308 goto raw;
309
310 } else {
311 /*
312 * Parse the string, and try to convert it to the
313 * underlying data type. If it can't be
314 * converted as a data type, just convert it as
315 * Argument-List.
316 *
317 * And if that fails, just ignore it completely.
318 */
319 if (fr_pair_value_from_str(vp, (char const *) value, arg_end - value, NULL, true) < 0) {
321 goto raw;
322 }
323
324 /*
325 * Else it parsed fine, append it to the output vendor list.
326 */
327 }
328
329 fr_pair_append(dst, vp);
330
331 } else {
332 raw:
334 if (!vp) {
335 oom:
336 fr_strerror_const("Out of Memory");
337 fail:
338 if (append) {
339 talloc_free(vendor);
340 } else {
342 }
343 return -1;
344 }
346
347 value = p;
348 arg_end = p + argv[i];
349
350 if ((arg_end > value) &&
351 (fr_pair_value_bstrndup(vp, (char const *) value, arg_end - value, true) < 0)) {
352 goto fail;
353 }
354
356 }
357
358 next:
359 p += argv[i];
360 }
361
362 if (append) {
363 if (fr_pair_list_num_elements(&vendor->vp_group) > 0) {
364 fr_pair_append(out, vendor);
365 } else {
366 talloc_free(vendor);
367 }
368 }
369
370 return 0;
371}
372
373/**
374 * Decode a TACACS+ field.
375 */
376static int tacacs_decode_field(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *da,
377 uint8_t const **field_data, uint16_t field_len, uint8_t const *end)
378{
379 uint8_t const *p = *field_data;
380 fr_pair_t *vp;
381
382 if (field_len > (end - p)) {
383 fr_strerror_printf("'%s' length %u overflows the remaining data (%zu) in the packet",
384 da->name, field_len, (size_t) (end - p));
385 return -1;
386 }
387
388 vp = fr_pair_afrom_da(ctx, da);
389 if (!vp) {
390 fr_strerror_const("Out of Memory");
391 return -1;
392 }
394
395 if (field_len) {
396 if (da->type == FR_TYPE_STRING) {
397 fr_pair_value_bstrndup(vp, (char const *)p, field_len, true);
398 } else if (da->type == FR_TYPE_OCTETS) {
399 fr_pair_value_memdup(vp, p, field_len, true);
400 } else {
401 fr_assert(0);
402 }
403 p += field_len;
404 *field_data = p;
405 }
406
408
409 return 0;
410}
411
412/**
413 * Decode a TACACS+ packet
414 */
416 uint8_t const *buffer, size_t buffer_len,
417 const uint8_t *original, char const * const secret, size_t secret_len, int *code)
418{
419 fr_tacacs_packet_t const *pkt;
420 fr_pair_t *vp;
421 size_t data_len;
422 uint8_t const *p, *body, *argv, *attrs, *end;
423 uint8_t *decrypted = NULL;
424
425 /*
426 * 3.4. The TACACS+ Packet Header
427 *
428 * 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
429 * +----------------+----------------+----------------+----------------+
430 * |major | minor | | | |
431 * |version| version| type | seq_no | flags |
432 * +----------------+----------------+----------------+----------------+
433 * | |
434 * | session_id |
435 * +----------------+----------------+----------------+----------------+
436 * | |
437 * | length |
438 * +----------------+----------------+----------------+----------------+
439 */
440 pkt = (fr_tacacs_packet_t const *) buffer;
441
442 /*
443 * p miscellaneous pointer for decoding things
444 * body points to just past the (randomly sized) per-packet header,
445 * where the various user / server messages are.
446 * sometimes this is after "argv".
447 * argv points to the array of argv[i] length entries
448 * attrs points to the attributes we need to decode as "foo=bar".
449 */
450 argv = attrs = NULL;
451 end = buffer + buffer_len;
452
453 /*
454 * Check that we have a full TACACS+ header before
455 * decoding anything.
456 */
457 if (buffer_len < sizeof(pkt->hdr)) {
458 fr_strerror_printf("Packet is too small (%zu < 12) to be TACACS+.", buffer_len);
459 return -1;
460 }
461
462 /*
463 * TACACS major / minor version MUST be 12.0 or 12.1
464 */
465 if (!(pkt->hdr.ver.major == 12 && (pkt->hdr.ver.minor == 0 || pkt->hdr.ver.minor == 1))) {
466 fr_strerror_printf("Unsupported TACACS+ version %d.%d (%02x)", pkt->hdr.ver.major, pkt->hdr.ver.minor, buffer[0]);
467 return -1;
468 }
469
470 /*
471 * There's no reason to accept 64K TACACS+ packets.
472 *
473 * In any case, the largest possible packet has the
474 * header, plus 2 16-bit fields, plus 255 8-bit fields,
475 * which is a bit under 2^18.
476 */
477 if ((buffer[8] != 0) || (buffer[9] != 0)) {
478 fr_strerror_const("Packet is too large. Our limit is 64K");
479 return -1;
480 }
481
482 /*
483 * As a stream protocol, the TACACS+ packet MUST fit
484 * exactly into however many bytes we read.
485 */
486 if ((buffer + sizeof(pkt->hdr) + ntohl(pkt->hdr.length)) != end) {
487 fr_strerror_const("Packet does not exactly fill buffer");
488 return -1;
489 }
490
491 /*
492 * There are only 3 types of packets which are supported.
493 */
494 if (!((pkt->hdr.type == FR_TAC_PLUS_AUTHEN) ||
495 (pkt->hdr.type == FR_TAC_PLUS_AUTHOR) ||
496 (pkt->hdr.type == FR_TAC_PLUS_ACCT))) {
497 fr_strerror_printf("Unknown packet type %d", pkt->hdr.type);
498 return -1;
499 }
500
501 /*
502 * Check that the session IDs are correct.
503 */
504 if (original && (memcmp(original + 4, buffer + 4, 4) != 0)) {
505 fr_strerror_printf("Session ID %08x does not match expected number %08x",
506 fr_nbo_to_uint32(buffer + 4), fr_nbo_to_uint32(original + 4));
507 return -1;
508 }
509
510 if (!secret && packet_is_encrypted(pkt)) {
511 fr_strerror_const("Packet is encrypted, but there is no secret to decrypt it");
512 return -1;
513 }
514
515 /*
516 * Call the struct encoder to do the actual work.
517 */
518 if (fr_struct_from_network(ctx, out, attr_tacacs_packet, buffer, buffer_len, NULL, NULL, NULL) < 0) {
519 fr_strerror_printf("Failed decoding TACACS header - %s", fr_strerror());
520 return -1;
521 }
522
523 /*
524 * 3.6. Encryption
525 *
526 * If there's a secret, we always decrypt the packets.
527 */
528 if (secret && packet_is_encrypted(pkt)) {
529 size_t length;
530
531 if (!secret_len) {
532 fr_strerror_const("Packet should be encrypted, but the secret has zero length");
533 return -1;
534 }
535
536 length = ntohl(pkt->hdr.length);
537
538 /*
539 * We need that to decrypt the body content.
540 *
541 * @todo - use thread-local storage to avoid allocations?
542 */
543 decrypted = talloc_memdup(ctx, buffer, buffer_len);
544 if (!decrypted) {
545 fr_strerror_const("Out of Memory");
546 return -1;
547 }
548
549 pkt = (fr_tacacs_packet_t const *) decrypted;
550 end = decrypted + buffer_len;
551
552 if (fr_tacacs_body_xor(pkt, decrypted + sizeof(pkt->hdr), length, secret, secret_len) < 0) {
553 fail:
554 talloc_free(decrypted);
555 return -1;
556 }
557
558 decrypted[3] |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
559
560 FR_PROTO_HEX_DUMP(decrypted, buffer_len, "fr_tacacs_packet_t (unencrypted)");
561
562 buffer = decrypted;
563 }
564
565#ifndef NDEBUG
567#endif
568
569 if (code) {
571 if (*code < 0) goto fail;
572 }
573
574 switch (pkt->hdr.type) {
577 uint8_t want;
578 bool raw;
579 fr_dict_attr_t const *da, *challenge;
580
581 /**
582 * 4.1. The Authentication START Packet Body
583 *
584 * 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
585 * +----------------+----------------+----------------+----------------+
586 * | action | priv_lvl | authen_type | authen_service |
587 * +----------------+----------------+----------------+----------------+
588 * | user_len | port_len | rem_addr_len | data_len |
589 * +----------------+----------------+----------------+----------------+
590 * | user ...
591 * +----------------+----------------+----------------+----------------+
592 * | port ...
593 * +----------------+----------------+----------------+----------------+
594 * | rem_addr ...
595 * +----------------+----------------+----------------+----------------+
596 * | data...
597 * +----------------+----------------+----------------+----------------+
598 */
599 PACKET_HEADER_CHECK("Authentication-Start", pkt->authen_start);
600
601 data_len += p[4] + p[5] + p[6] + p[7];
602 if (data_len > (size_t) (end - p)) {
603 overflow:
604 if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) {
605 bad_secret:
606 fr_strerror_const("Invalid packet after decryption - is the secret key incorrect?");
607 goto fail;
608 }
609
610 fr_strerror_const("Data overflows the packet");
611 goto fail;
612 }
613 if (data_len < (size_t) (end - p)) {
614 underflow:
615 if ((buffer[3] & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) goto bad_secret;
616
617 fr_strerror_const("Data underflows the packet");
618 goto fail;
619 }
620
622
623 /*
624 * Decode 4 octets of various flags.
625 */
626 DECODE_FIELD_UINT8(attr_tacacs_action, pkt->authen_start.action);
627 DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->authen_start.priv_lvl);
628 DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->authen_start.authen_type);
629 DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->authen_start.authen_service);
630
631 /*
632 * Decode 3 fields, based on their "length"
633 * user and rem_addr are optional - indicated by zero length
634 */
635 p = body;
636 if (pkt->authen_start.user_len > 0) DECODE_FIELD_STRING8(attr_tacacs_user_name,
637 pkt->authen_start.user_len);
638 DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->authen_start.port_len);
639 if (pkt->authen_start.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
640 pkt->authen_start.rem_addr_len);
641
642 /*
643 * Check the length on the various
644 * authentication types.
645 */
646 raw = false;
647 challenge = NULL;
648
649 switch (pkt->authen_start.authen_type) {
650 default:
651 raw = true;
652 want = pkt->authen_start.data_len;
653 da = attr_tacacs_data;
654 break;
655
656 case FR_AUTHENTICATION_TYPE_VALUE_PAP:
657 want = pkt->authen_start.data_len;
659 break;
660
661 case FR_AUTHENTICATION_TYPE_VALUE_CHAP:
662 want = 1 + 16; /* id + HOPEFULLY 8 octets of challenge + 16 hash */
664 challenge = attr_tacacs_chap_challenge;
665 break;
666
667 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
668 want = 1 + 49; /* id + HOPEFULLY 8 octets of challenge + 49 MS-CHAP stuff */
671 break;
672
673 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
674 want = 1 + 49; /* id + HOPEFULLY 16 octets of challenge + 49 MS-CHAP stuff */
677 break;
678 }
679
680 /*
681 * If we have enough data, decode it as
682 * the claimed authentication type.
683 *
684 * Otherwise, decode the entire field as an unknown
685 * attribute.
686 */
687 if (raw || (pkt->authen_start.data_len < want)) {
688 fr_dict_attr_t *da_unknown;
689
691 attr_tacacs_data->attr);
692 if (!da_unknown) goto fail;
693
694 want = pkt->authen_start.data_len;
695
696 DECODE_FIELD_STRING8(da_unknown, want);
697 talloc_free(da_unknown);
698
699 } else if (!challenge) {
700 DECODE_FIELD_STRING8(da, want);
701
702 } else if (pkt->authen_start.data_len == want) {
703 fr_strerror_printf("%s has zero length", challenge->name);
704 goto fail;
705
706 } else { /* 1 of ID + ??? of challenge + (want-1) of data */
707 uint8_t challenge_len = pkt->authen_start.data_len - want;
708 uint8_t hash[50];
709
710 /*
711 * Rework things to make sense.
712 * RFC 8079 says that MS-CHAP responses should follow RFC 2433 and 2759
713 * which have "Flags" at the end.
714 * RADIUS attributes expect "Flags" after the ID as per RFC 2548.
715 * Re-arrange to make things consistent.
716 */
717 hash[0] = p[0];
718 switch (pkt->authen_start.authen_type) {
719 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
720 case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
721 hash[1] = p[want - 1];
722 memcpy(hash + 2, p + 1 + challenge_len, want - 2);
723 break;
724
725 default:
726 memcpy(hash + 1, p + 1 + challenge_len, want - 1);
727 break;
728 }
729
730 vp = fr_pair_afrom_da(ctx, da);
731 if (!vp) goto fail;
733
735
736 /*
737 * ID + hash
738 */
739 if (fr_pair_value_memdup(vp, hash, want, true) < 0) goto fail;
740
741 /*
742 * And then the challenge.
743 */
744 vp = fr_pair_afrom_da(ctx, challenge);
745 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 talloc_free(decrypted);
1072 return buffer_len;
1073}
1074
1075/*
1076 * Test points for protocol decode
1077 */
1078static 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)
1079{
1080 fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1081 fr_dict_attr_t const *dv;
1082
1083 dv = fr_dict_attr_by_name(NULL, fr_dict_root(dict_tacacs), "Test");
1084 fr_assert(!dv || (dv->type == FR_TYPE_VENDOR));
1085
1086 return fr_tacacs_decode(ctx, out, dv, data, data_len, NULL,
1087 test_ctx->secret, (talloc_array_length(test_ctx->secret)-1), NULL);
1088}
1089
1090static int _encode_test_ctx(fr_tacacs_ctx_t *proto_ctx)
1091{
1092 talloc_const_free(proto_ctx->secret);
1093
1095
1096 return 0;
1097}
1098
1099static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
1100 UNUSED fr_dict_attr_t const *root_da)
1101{
1102 fr_tacacs_ctx_t *test_ctx;
1103
1104 if (fr_tacacs_global_init() < 0) return -1;
1105
1106 test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1107 if (!test_ctx) return -1;
1108
1109 test_ctx->secret = talloc_strdup(test_ctx, "testing123");
1110 test_ctx->root = fr_dict_root(dict_tacacs);
1111 talloc_set_destructor(test_ctx, _encode_test_ctx);
1112
1113 *out = test_ctx;
1114
1115 return 0;
1116}
1117
static int const char char buffer[256]
Definition acutest.h:578
#define NDEBUG_UNUSED
Definition build.h:328
#define UNUSED
Definition build.h:317
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:3567
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:616
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2704
Test enumeration values.
Definition dict_test.h:92
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
fr_log_t default_log
Definition log.c:288
@ 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:1192
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:2945
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:2599
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:703
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:1348
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:289
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:2795
static fr_dict_attr_t const * attr_tacacs_authentication_flags
Definition base.c:56
static fr_dict_attr_t const * attr_tacacs_user_message
Definition base.c:73
static fr_dict_attr_t const * attr_tacacs_server_message
Definition base.c:69
static fr_dict_attr_t const * attr_tacacs_privilege_level
Definition base.c:67
static fr_dict_attr_t const * attr_tacacs_authentication_type
Definition base.c:57
static fr_dict_attr_t const * attr_tacacs_accounting_status
Definition base.c:62
static fr_dict_attr_t const * attr_tacacs_authentication_service
Definition base.c:58
static fr_dict_attr_t const * attr_tacacs_authentication_status
Definition base.c:59
static fr_dict_attr_t const * attr_tacacs_client_port
Definition base.c:65
static fr_dict_attr_t const * attr_tacacs_remote_address
Definition base.c:68
static fr_dict_attr_t const * attr_tacacs_action
Definition base.c:54
static fr_dict_attr_t const * attr_tacacs_authorization_status
Definition base.c:61
static fr_dict_attr_t const * attr_tacacs_accounting_flags
Definition base.c:63
static fr_dict_attr_t const * attr_tacacs_data
Definition base.c:66
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:149
void fr_tacacs_global_free(void)
Definition base.c:613
int fr_tacacs_global_init(void)
Definition base.c:589
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:182
static int _encode_test_ctx(fr_tacacs_ctx_t *proto_ctx)
Definition decode.c:1090
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:415
#define DECODE_FIELD_UINT8(_da, _field)
Definition decode.c:159
fr_test_point_proto_decode_t tacacs_tp_decode_proto
Definition decode.c:1119
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:1078
#define BODY(_x)
Definition decode.c:177
#define PACKET_HEADER_CHECK(_msg, _hdr)
Definition decode.c:127
#define DECODE_FIELD_STRING16(_da, _field)
Definition decode.c:172
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:376
#define DECODE_FIELD_STRING8(_da, _field)
Definition decode.c:167
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: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:229
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:212
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:857
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition proto.h:42
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#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:1326
static size_t char ** out
Definition value.h:1023