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: ded0a02c987dc9deebaf8daed7ea73f8ae4245fc $
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, true, 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  if (!da_unknown) goto fail;
686 
687  want = pkt->authen_start.data_len;
688 
689  DECODE_FIELD_STRING8(da_unknown, want);
690  talloc_free(da_unknown);
691 
692  } else if (!challenge) {
693  DECODE_FIELD_STRING8(da, want);
694 
695  } else if (pkt->authen_start.data_len == want) {
696  fr_strerror_printf("%s has zero length", challenge->name);
697  goto fail;
698 
699  } else { /* 1 of ID + ??? of challenge + (want-1) of data */
700  uint8_t challenge_len = pkt->authen_start.data_len - want;
701  uint8_t hash[50];
702 
703  /*
704  * Rework things to make sense.
705  * RFC 8079 says that MS-CHAP responses should follow RFC 2433 and 2759
706  * which have "Flags" at the end.
707  * RADIUS attributes expect "Flags" after the ID as per RFC 2548.
708  * Re-arrange to make things consistent.
709  */
710  hash[0] = p[0];
711  switch (pkt->authen_start.authen_type) {
712  case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP:
713  case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2:
714  hash[1] = p[want - 1];
715  memcpy(hash + 2, p + 1 + challenge_len, want - 2);
716  break;
717 
718  default:
719  memcpy(hash + 1, p + 1 + challenge_len, want - 1);
720  break;
721  }
722 
723  vp = fr_pair_afrom_da(ctx, da);
724  if (!vp) goto fail;
725 
727 
728  /*
729  * ID + hash
730  */
731  if (fr_pair_value_memdup(vp, hash, want, true) < 0) goto fail;
732 
733  /*
734  * And then the challenge.
735  */
736  vp = fr_pair_afrom_da(ctx, challenge);
737  if (!vp) goto fail;
738 
740 
741  if (fr_pair_value_memdup(vp, p + 1, challenge_len, true) < 0) goto fail;
742 
743  p += pkt->authen_start.data_len;
744  }
745 
746  } else if (packet_is_authen_continue(pkt)) {
747  /*
748  * 4.3. The Authentication CONTINUE Packet Body
749  *
750  * This packet is sent from the client to the server following the receipt of
751  * a REPLY packet.
752  *
753  * 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
754  * +----------------+----------------+----------------+----------------+
755  * | user_msg len | data_len |
756  * +----------------+----------------+----------------+----------------+
757  * | flags | user_msg ...
758  * +----------------+----------------+----------------+----------------+
759  * | data ...
760  * +----------------+
761  */
762 
763  /*
764  * Version 1 is ONLY used for PAP / CHAP
765  * / MS-CHAP start and reply packets.
766  */
767  if (pkt->hdr.ver.minor != 0) {
768  invalid_version:
769  fr_strerror_const("Invalid TACACS+ version");
770  goto fail;
771  }
772 
773  PACKET_HEADER_CHECK("Authentication-Continue", pkt->authen_cont);
774  data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
775  if (data_len > (size_t) (end - p)) goto overflow;
776  if (data_len < (size_t) (end - p)) goto underflow;
777 
779 
780  /*
781  * Decode 2 fields, based on their "length"
782  */
783  p = body;
784  DECODE_FIELD_STRING16(attr_tacacs_user_message, pkt->authen_cont.user_msg_len);
785  DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_cont.data_len);
786 
787  /*
788  * And finally the flags.
789  */
791 
792  } else if (packet_is_authen_reply(pkt)) {
793  /*
794  * 4.2. The Authentication REPLY Packet Body
795  *
796  * 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
797  * +----------------+----------------+----------------+----------------+
798  * | status | flags | server_msg_len |
799  * +----------------+----------------+----------------+----------------+
800  * | data_len | server_msg ...
801  * +----------------+----------------+----------------+----------------+
802  * | data ...
803  * +----------------+----------------+
804  */
805 
806  /*
807  * We don't care about versions for replies.
808  * We just echo whatever was sent in the request.
809  */
810  PACKET_HEADER_CHECK("Authentication-Reply", pkt->authen_reply);
811  data_len += fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
812  if (data_len > (size_t) (end - p)) goto overflow;
813  if (data_len < (size_t) (end - p)) goto underflow;
814 
816 
817  DECODE_FIELD_UINT8(attr_tacacs_authentication_status, pkt->authen_reply.status);
818  DECODE_FIELD_UINT8(attr_tacacs_authentication_flags, pkt->authen_reply.flags);
819 
820  /*
821  * Decode 2 fields, based on their "length"
822  */
823  p = body;
824  DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->authen_reply.server_msg_len);
825  DECODE_FIELD_STRING16(attr_tacacs_data, pkt->authen_reply.data_len);
826 
827  } else {
828  fr_strerror_const("Unknown authentication packet");
829  goto fail;
830  }
831  break;
832 
833  case FR_TAC_PLUS_AUTHOR:
834  if (packet_is_author_request(pkt)) {
835  /*
836  * 5.1. The Authorization REQUEST Packet Body
837  *
838  * 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
839  * +----------------+----------------+----------------+----------------+
840  * | authen_method | priv_lvl | authen_type | authen_service |
841  * +----------------+----------------+----------------+----------------+
842  * | user_len | port_len | rem_addr_len | arg_cnt |
843  * +----------------+----------------+----------------+----------------+
844  * | arg_1_len | arg_2_len | ... | arg_N_len |
845  * +----------------+----------------+----------------+----------------+
846  * | user ...
847  * +----------------+----------------+----------------+----------------+
848  * | port ...
849  * +----------------+----------------+----------------+----------------+
850  * | rem_addr ...
851  * +----------------+----------------+----------------+----------------+
852  * | arg_1 ...
853  * +----------------+----------------+----------------+----------------+
854  * | arg_2 ...
855  * +----------------+----------------+----------------+----------------+
856  * | ...
857  * +----------------+----------------+----------------+----------------+
858  * | arg_N ...
859  * +----------------+----------------+----------------+----------------+
860  */
861 
862  if (pkt->hdr.ver.minor != 0) goto invalid_version;
863 
864  PACKET_HEADER_CHECK("Authorization-Request", pkt->author_req);
865  data_len += p[4] + p[5] + p[6] + p[7];
866 
867  ARG_COUNT_CHECK("Authorization-Request", pkt->author_req);
868 
870 
871  /*
872  * Decode 4 octets of various flags.
873  */
874  DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->author_req.authen_method);
875  DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->author_req.priv_lvl);
876  DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->author_req.authen_type);
877  DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->author_req.authen_service);
878 
879  /*
880  * Decode 3 fields, based on their "length"
881  * rem_addr is optional - indicated by zero length
882  */
883  p = body;
884  DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->author_req.user_len);
885  DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->author_req.port_len);
886  if (pkt->author_req.rem_addr_len > 0) DECODE_FIELD_STRING8(attr_tacacs_remote_address,
887  pkt->author_req.rem_addr_len);
888 
889  /*
890  * Decode 'arg_N' arguments (horrible format)
891  */
892  if (tacacs_decode_args(ctx, out, vendor,
893  pkt->author_req.arg_cnt, argv, attrs, end) < 0) goto fail;
894 
895  } else if (packet_is_author_reply(pkt)) {
896  /*
897  * 5.2. The Authorization RESPONSE Packet Body
898  *
899  * 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
900  * +----------------+----------------+----------------+----------------+
901  * | status | arg_cnt | server_msg len |
902  * +----------------+----------------+----------------+----------------+
903  * + data_len | arg_1_len | arg_2_len |
904  * +----------------+----------------+----------------+----------------+
905  * | ... | arg_N_len | server_msg ...
906  * +----------------+----------------+----------------+----------------+
907  * | data ...
908  * +----------------+----------------+----------------+----------------+
909  * | arg_1 ...
910  * +----------------+----------------+----------------+----------------+
911  * | arg_2 ...
912  * +----------------+----------------+----------------+----------------+
913  * | ...
914  * +----------------+----------------+----------------+----------------+
915  * | arg_N ...
916  * +----------------+----------------+----------------+----------------+
917  */
918 
919  /*
920  * We don't care about versions for replies.
921  * We just echo whatever was sent in the request.
922  */
923 
924  PACKET_HEADER_CHECK("Authorization-Reply", pkt->author_reply);
925  data_len += p[1] + fr_nbo_to_uint16(p + 2) + fr_nbo_to_uint16(p + 4);
926 
927  ARG_COUNT_CHECK("Authorization-Reply", pkt->author_reply);
929 
930  /*
931  * Decode 1 octets
932  */
933  DECODE_FIELD_UINT8(attr_tacacs_authorization_status, pkt->author_reply.status);
934 
935  /*
936  * Decode 2 fields, based on their "length"
937  */
938  p = body;
939  DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->author_reply.server_msg_len);
940  DECODE_FIELD_STRING16(attr_tacacs_data, pkt->author_reply.data_len);
941 
942  /*
943  * Decode 'arg_N' arguments (horrible format)
944  */
945  if (tacacs_decode_args(ctx, out, vendor,
946  pkt->author_reply.arg_cnt, argv, attrs, end) < 0) goto fail;
947 
948  } else {
949  fr_strerror_const("Unknown authorization packet");
950  goto fail;
951  }
952  break;
953 
954  case FR_TAC_PLUS_ACCT:
955  if (packet_is_acct_request(pkt)) {
956  /**
957  * 6.1. The Account REQUEST Packet Body
958  *
959  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
960  * +----------------+----------------+----------------+----------------+
961  * | flags | authen_method | priv_lvl | authen_type |
962  * +----------------+----------------+----------------+----------------+
963  * | authen_service | user_len | port_len | rem_addr_len |
964  * +----------------+----------------+----------------+----------------+
965  * | arg_cnt | arg_1_len | arg_2_len | ... |
966  * +----------------+----------------+----------------+----------------+
967  * | arg_N_len | user ...
968  * +----------------+----------------+----------------+----------------+
969  * | port ...
970  * +----------------+----------------+----------------+----------------+
971  * | rem_addr ...
972  * +----------------+----------------+----------------+----------------+
973  * | arg_1 ...
974  * +----------------+----------------+----------------+----------------+
975  * | arg_2 ...
976  * +----------------+----------------+----------------+----------------+
977  * | ...
978  * +----------------+----------------+----------------+----------------+
979  * | arg_N ...
980  * +----------------+----------------+----------------+----------------+
981  */
982 
983  if (pkt->hdr.ver.minor != 0) goto invalid_version;
984 
985  PACKET_HEADER_CHECK("Accounting-Request", pkt->acct_req);
986  data_len += p[5] + p[6] + p[7] + p[8];
987  if (data_len > (size_t) (end - p)) goto overflow;
988  /* can't check for underflow, as we have argv[argc] */
989 
990  ARG_COUNT_CHECK("Accounting-Request", pkt->acct_req);
991 
993 
994  /*
995  * Decode 5 octets of various flags.
996  */
997  DECODE_FIELD_UINT8(attr_tacacs_accounting_flags, pkt->acct_req.flags);
998  DECODE_FIELD_UINT8(attr_tacacs_authentication_method, pkt->acct_req.authen_method);
999  DECODE_FIELD_UINT8(attr_tacacs_privilege_level, pkt->acct_req.priv_lvl);
1000  DECODE_FIELD_UINT8(attr_tacacs_authentication_type, pkt->acct_req.authen_type);
1001  DECODE_FIELD_UINT8(attr_tacacs_authentication_service, pkt->acct_req.authen_service);
1002 
1003  /*
1004  * Decode 3 fields, based on their "length"
1005  */
1006  p = body;
1007  DECODE_FIELD_STRING8(attr_tacacs_user_name, pkt->acct_req.user_len);
1008  DECODE_FIELD_STRING8(attr_tacacs_client_port, pkt->acct_req.port_len);
1009  DECODE_FIELD_STRING8(attr_tacacs_remote_address, pkt->acct_req.rem_addr_len);
1010 
1011  /*
1012  * Decode 'arg_N' arguments (horrible format)
1013  */
1014  if (tacacs_decode_args(ctx, out, vendor,
1015  pkt->acct_req.arg_cnt, argv, attrs, end) < 0) goto fail;
1016 
1017  } else if (packet_is_acct_reply(pkt)) {
1018  /**
1019  * 6.2. The Accounting REPLY Packet Body
1020  *
1021  * 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
1022  * +----------------+----------------+----------------+----------------+
1023  * | server_msg len | data_len |
1024  * +----------------+----------------+----------------+----------------+
1025  * | status | server_msg ...
1026  * +----------------+----------------+----------------+----------------+
1027  * | data ...
1028  * +----------------+
1029  */
1030 
1031  /*
1032  * We don't care about versions for replies.
1033  * We just echo whatever was sent in the request.
1034  */
1035 
1036  PACKET_HEADER_CHECK("Accounting-Reply", pkt->acct_reply);
1037  data_len += fr_nbo_to_uint16(p) + fr_nbo_to_uint16(p + 2);
1038  if (data_len > (size_t) (end - p)) goto overflow;
1039  if (data_len < (size_t) (end - p)) goto underflow;
1040 
1041  p = BODY(acct_reply);
1043 
1044  /*
1045  * Decode 2 fields, based on their "length"
1046  */
1047  DECODE_FIELD_STRING16(attr_tacacs_server_message, pkt->acct_reply.server_msg_len);
1048  DECODE_FIELD_STRING16(attr_tacacs_data, pkt->acct_reply.data_len);
1049 
1050  /* Decode 1 octet */
1051  DECODE_FIELD_UINT8(attr_tacacs_accounting_status, pkt->acct_reply.status);
1052  } else {
1053  fr_strerror_const("Unknown accounting packet");
1054  goto fail;
1055  }
1056  break;
1057  default:
1058  fr_strerror_printf("decode: Unsupported packet type %u", pkt->hdr.type);
1059  goto fail;
1060  }
1061 
1062  talloc_free(decrypted);
1063  return buffer_len;
1064 }
1065 
1066 /*
1067  * Test points for protocol decode
1068  */
1069 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)
1070 {
1071  fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1072  fr_dict_attr_t const *dv;
1073 
1074  dv = fr_dict_attr_by_name(NULL, fr_dict_root(dict_tacacs), "Test");
1075  fr_assert(!dv || (dv->type == FR_TYPE_VENDOR));
1076 
1077  return fr_tacacs_decode(ctx, out, dv, data, data_len, NULL,
1078  test_ctx->secret, (talloc_array_length(test_ctx->secret)-1), false);
1079 }
1080 
1081 static int _encode_test_ctx(fr_tacacs_ctx_t *proto_ctx)
1082 {
1083  talloc_const_free(proto_ctx->secret);
1084 
1086 
1087  return 0;
1088 }
1089 
1090 static int decode_test_ctx(void **out, TALLOC_CTX *ctx)
1091 {
1092  fr_tacacs_ctx_t *test_ctx;
1093 
1094  if (fr_tacacs_global_init() < 0) return -1;
1095 
1096  test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1097  if (!test_ctx) return -1;
1098 
1099  test_ctx->secret = talloc_strdup(test_ctx, "testing123");
1100  test_ctx->root = fr_dict_root(dict_tacacs);
1101  talloc_set_destructor(test_ctx, _encode_test_ctx);
1102 
1103  *out = test_ctx;
1104 
1105  return 0;
1106 }
1107 
1111  .func = fr_tacacs_decode_proto
1112 };
static int const char char buffer[256]
Definition: acutest.h:574
#define NDEBUG_UNUSED
Definition: build.h:324
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:2860
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
fr_dict_attr_t * fr_dict_unknown_attr_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da))
Initialise an octets type attribute from a da.
Definition: dict_unknown.c:378
Test enumeration values.
Definition: dict_test.h:92
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:42
fr_log_t default_log
Definition: log.c:290
@ 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:137
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:158
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:688
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:278
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:2978
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:1340
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:2781
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:2586
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:1081
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:1109
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:1069
#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:1090
#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, bool nested, 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:212
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:66
Entry point for protocol decoders.
Definition: test_point.h:65
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:844
#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:1259
static size_t char ** out
Definition: value.h:984