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