The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
encode.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: 9cde8cf54fa82c01b2a234820daf100db7a7e35c $
19  *
20  * @file protocols/tacacs/encode.c
21  * @brief Low-Level TACACS+ encode 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/dbuff.h>
30 #include <freeradius-devel/util/rand.h>
31 #include <freeradius-devel/util/struct.h>
32 
33 #include "tacacs.h"
34 #include "attrs.h"
35 
37 {
38  switch (code) {
39  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_START:
41  pkt->hdr.seq_no = 1;
42  break;
43 
44  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_PASS:
46  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_PASS;
47  break;
48 
49  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_FAIL:
51  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_FAIL;
52  break;
53 
54  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETDATA:
56  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETDATA;
57  break;
58 
59  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETUSER:
61  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETUSER;
62  break;
63 
64  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_GETPASS:
66  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_GETPASS;
67  break;
68 
69  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_RESTART:
71  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_RESTART;
72  break;
73 
74  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_ERROR:
76  pkt->authen_reply.status = FR_TAC_PLUS_AUTHEN_STATUS_ERROR;
77  break;
78 
79  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE:
81  pkt->authen_cont.flags = FR_TAC_PLUS_CONTINUE_FLAG_UNSET;
82  break;
83 
84  case FR_PACKET_TYPE_VALUE_AUTHENTICATION_CONTINUE_ABORT:
86  pkt->authen_cont.flags = FR_TAC_PLUS_CONTINUE_FLAG_ABORT;
87  break;
88 
89  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_REQUEST:
91  break;
92 
93  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_PASS_ADD:
95  pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_PASS_ADD;
96  break;
97 
98  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_PASS_REPLACE:
100  pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_PASS_REPL;
101  break;
102 
103  case FR_PACKET_TYPE_VALUE_AUTHORIZATION_FAIL:
104  pkt->hdr.type = FR_TAC_PLUS_AUTHOR;
105  pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_FAIL;
106  break;
107 
108  case FR_PACKET_TYPE_VALUE_ACCOUNTING_REQUEST:
109  pkt->hdr.type = FR_TAC_PLUS_ACCT;
110  break;
111 
112  case FR_PACKET_TYPE_VALUE_ACCOUNTING_SUCCESS:
113  pkt->hdr.type = FR_TAC_PLUS_ACCT;
114  pkt->acct_reply.status = FR_TAC_PLUS_ACCT_STATUS_SUCCESS;
115  break;
116 
117  case FR_PACKET_TYPE_VALUE_ACCOUNTING_ERROR:
118  pkt->hdr.type = FR_TAC_PLUS_ACCT;
119  pkt->acct_reply.status = FR_TAC_PLUS_ACCT_STATUS_ERROR;
120  break;
121 
122  default:
123  fr_strerror_const("Invalid TACACS+ packet type");
124  return -1;
125  }
126 
127  return 0;
128 }
129 
130 /**
131  * Encode a TACACS+ 'arg_N' fields.
132  */
134 {
135  int arg_cnt = 0;
136  fr_pair_t *vp;
137 
138  for (vp = fr_pair_list_head(vps);
139  vp;
140  vp = fr_pair_list_next(vps, vp)) {
141  if (arg_cnt == 255) break;
142 
143  if (vp->da->flags.internal) continue;
144 
145  if (vp->da == attr_tacacs_packet) continue;
146 
147  /*
148  * Argument-List = "foo=bar"
149  */
150  if (vp->da == da) {
151  if (vp->vp_length > 0xff) continue;
152  arg_cnt++;
153  continue;
154  }
155 
157 
158  /*
159  * RFC 8907 attributes.
160  */
161  if (vp->da->parent->flags.is_root) {
162  arg_cnt++;
163  continue;
164  }
165 
166  /*
167  * Recurse into children.
168  */
169  if (vp->vp_type == FR_TYPE_VENDOR) {
170  arg_cnt += tacacs_encode_body_arg_cnt(&vp->vp_group, NULL);
171  continue;
172  }
173 
174  if (vp->da->parent->type != FR_TYPE_VENDOR) continue;
175 
176  arg_cnt++;
177  }
178 
179  return arg_cnt;
180 }
181 
182 static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint8_t *arg_len, fr_pair_list_t *vps, fr_dict_attr_t const *da)
183 {
184  fr_pair_t *vp;
185  uint8_t i = 0;
186  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
187 
188  for (vp = fr_pair_list_head(vps);
189  vp;
190  vp = fr_pair_list_next(vps, vp)) {
191  int len;
192 
193  if (i == 255) break;
194  if (i > arg_cnt) break;
195 
196  if (vp->da->flags.internal) continue;
197 
198  if (vp->da == attr_tacacs_packet) continue;
199 
200  /*
201  * Argument-List = "foo=bar"
202  */
203  if (vp->da == da) {
204  if (vp->vp_length > 0xff) continue;
205 
206  /* Append the <arg_N> field */
207  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
208 
209  FR_PROTO_TRACE("arg[%d] --> %s", i, vp->vp_strvalue);
210  len = vp->vp_length;
211 
212  } else if (vp->vp_type == FR_TYPE_VENDOR) {
213  ssize_t slen;
214  uint8_t child_argc;
215 
216  /*
217  * Nested attribute: just recurse.
218  */
219  child_argc = fr_pair_list_num_elements(&vp->vp_group);
220  if (child_argc > (arg_cnt - i)) child_argc = arg_cnt = i;
221 
222  slen = tacacs_encode_body_arg_n(&work_dbuff, child_argc, &arg_len[i], &vp->vp_group, vp->da);
223  if (slen < 0) return slen - fr_dbuff_used(&work_dbuff);
224 
225  i += child_argc;
226  continue;
227 
228  } else if (!vp->da->parent || (!vp->da->parent->flags.is_root && (vp->da->parent->type != FR_TYPE_VENDOR))) {
229  continue;
230 
231  } else {
232  ssize_t slen;
233  fr_sbuff_t sbuff;
234  fr_dbuff_t arg_dbuff = FR_DBUFF_MAX(&work_dbuff, 255);
235  fr_value_box_t box;
236  char buffer[256];
237 
238  /*
239  * Print it as "name=value"
240  */
241  FR_DBUFF_IN_MEMCPY_RETURN(&arg_dbuff, vp->da->name, strlen(vp->da->name));
242  FR_DBUFF_IN_BYTES_RETURN(&arg_dbuff, (uint8_t) '=');
243 
244  sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
245 
246  switch (vp->vp_type) {
247  /*
248  * For now, we always print time deltas and dates as integers.
249  *
250  * Because everyone else's date formats are insane.
251  */
252  case FR_TYPE_DATE:
253  case FR_TYPE_TIME_DELTA:
254  fr_value_box_init(&box, FR_TYPE_UINT64, vp->data.enumv, vp->vp_tainted);
255  if (fr_value_box_cast(NULL, &box, FR_TYPE_UINT64, NULL, &vp->data) < 0) {
256  buffer[0] = '\0';
257  slen = 0;
258  break;
259  }
260 
261  slen = fr_sbuff_in_sprintf(&sbuff, "%lu", box.vb_uint64);
262  if (slen <= 0) return -1;
263  break;
264 
265  default:
266  slen = fr_pair_print_value_quoted(&sbuff, vp, T_BARE_WORD);
267  if (slen <= 0) return -1;
268  }
269 
270  FR_DBUFF_IN_MEMCPY_RETURN(&arg_dbuff, buffer, (size_t) slen);
271 
272  len = fr_dbuff_used(&arg_dbuff);
273 
274  FR_PROTO_TRACE("arg[%d] --> %.*s", i, len, fr_dbuff_start(&arg_dbuff));
275 
276  fr_dbuff_set(&work_dbuff, &arg_dbuff);
277  }
278 
279  fr_assert(len <= UINT8_MAX);
280 
281  FR_PROTO_TRACE("len(arg[%d]) = %d", i, len);
282  arg_len[i++] = len;
283  }
284 
285  return fr_dbuff_set(dbuff, &work_dbuff);
286 }
287 
288 /*
289  * Encode a TACACS+ field.
290  */
291 static ssize_t tacacs_encode_field(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_dict_attr_t const *da, size_t max_len)
292 {
293  fr_pair_t *vp;
294  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
295 
296  vp = fr_pair_find_by_da(vps, NULL, da);
297  if (!vp || !vp->vp_length || (vp->vp_length > max_len)) return 0;
298 
299  if (da->type == FR_TYPE_STRING) {
300  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
301  } else {
302  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_octets, vp->vp_length);
303  }
304 
305  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), da->name);
306 
307  return fr_dbuff_set(dbuff, &work_dbuff);
308 }
309 
310 static ssize_t tacacs_encode_chap(fr_dbuff_t *dbuff, fr_tacacs_packet_t *packet, fr_pair_list_t *vps, fr_dict_attr_t const *da_chap, fr_dict_attr_t const *da_challenge)
311 {
312  fr_pair_t *chap, *challenge;
313  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
314 
315  chap = fr_pair_find_by_da(vps, NULL, da_chap);
316  if (!chap) {
317  packet->authen_start.data_len = 0;
318  return 0;
319  }
320 
321  challenge = fr_pair_find_by_da(vps, NULL, da_challenge);
322  if (!challenge) {
323  fr_strerror_printf("Packet contains %s but no %s", da_chap->name, da_challenge->name);
324  return -1;
325  }
326 
327  if (!challenge->vp_length) {
328  fr_strerror_printf("%s is empty", da_challenge->name);
329  return -1;
330  }
331 
332  if ((chap->vp_length + challenge->vp_length) > 255) {
333  fr_strerror_printf("%s and %s are longer than 255 octets", da_chap->name, da_challenge->name);
334  return -1;
335  }
336 
337  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, chap->vp_octets, 1);
338  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, challenge->vp_octets, challenge->vp_length);
339  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, chap->vp_octets + 1, chap->vp_length - 1);
340 
341  packet->authen_start.data_len = chap->vp_length + challenge->vp_length;
342 
343  return fr_dbuff_set(dbuff, &work_dbuff);
344 }
345 
346 /*
347  * Magic macros to keep things happy.
348  *
349  * Note that the various fields are optional. If the caller
350  * doesn't specify them, then they don't get encoded.
351  */
352 #define ENCODE_FIELD_UINT8(_field, _da) do { \
353  vp = fr_pair_find_by_da(vps, NULL, _da); \
354  _field = (vp) ? vp->vp_uint8 : 0; \
355 } while (0)
356 
357 #define ENCODE_FIELD_STRING8(_field, _da) _field = tacacs_encode_field(&work_dbuff, vps, _da, 0xff)
358 #define ENCODE_FIELD_STRING16(_field, _da) _field = htons(tacacs_encode_field(&work_dbuff, vps, _da, 0xffff))
359 
360 /**
361  * Encode VPS into a raw TACACS packet.
362  */
363 ssize_t fr_tacacs_encode(fr_dbuff_t *dbuff, uint8_t const *original_packet, char const *secret, size_t secret_len,
364  unsigned int code, fr_pair_list_t *vps)
365 {
366  fr_pair_t *vp;
367  fr_tacacs_packet_t *packet;
368  fr_dcursor_t cursor;
369  fr_da_stack_t da_stack;
370  ssize_t len = 0;
371  size_t body_len, packet_len;
372  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
373  fr_dbuff_marker_t hdr, body, hdr_io;
374  uint8_t version_byte = 0;
375 
376  fr_tacacs_packet_hdr_t const *original = (fr_tacacs_packet_hdr_t const *) original_packet;
377 
378  if (!vps) {
379  error:
380  fr_strerror_const("Cannot encode empty packet");
381  return -1;
382  }
383 
384  /*
385  * Verify space for the packet...
386  */
387  FR_DBUFF_REMAINING_RETURN(&work_dbuff, sizeof(fr_tacacs_packet_t));
388 
389  /*
390  * 3.4. The TACACS+ Packet Header
391  *
392  * 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
393  * +----------------+----------------+----------------+----------------+
394  * |major | minor | | | |
395  * |version| version| type | seq_no | flags |
396  * +----------------+----------------+----------------+----------------+
397  * | |
398  * | session_id |
399  * +----------------+----------------+----------------+----------------+
400  * | |
401  * | length |
402  * +----------------+----------------+----------------+----------------+
403  */
404 
405  /*
406  * Let's keep reference for packet header.
407  */
408  fr_dbuff_marker(&hdr, &work_dbuff);
409  /*
410  * Add marker letting us read/write header bytes without moving hdr.
411  */
412  fr_dbuff_marker(&hdr_io, &work_dbuff);
413 
414  /*
415  * Handle the fields in-place.
416  */
417  packet = (fr_tacacs_packet_t *)fr_dbuff_start(&work_dbuff);
418 
419  /*
420  * Find the first attribute which is parented by TACACS-Packet.
421  */
422  for (vp = fr_pair_dcursor_init(&cursor, vps);
423  vp;
424  vp = fr_dcursor_next(&cursor)) {
425  if (vp->da == attr_tacacs_packet) break;
426  if (vp->da->parent == attr_tacacs_packet) break;
427  }
428 
429  /*
430  * No "Packet" struct to encode. We MUST have an original packet to copy the various fields
431  * from.
432  */
433  if (!vp) {
434  if (!original) {
435  fr_strerror_printf("%s: No TACACS+ %s in the attribute list",
436  __FUNCTION__, attr_tacacs_packet->name);
437  return -1;
438  }
439 
440  /*
441  * Initialize the buffer avoiding invalid values.
442  */
443  memset(packet, 0, sizeof(fr_tacacs_packet_t));
444 
445  /*
446  * Initialize the reply from the request.
447  *
448  * Make room and fill up the original header. We shouldn't just copy the original packet,
449  * because the fields 'seq_no' and 'length' are not the same.
450  */
451  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(fr_tacacs_packet_hdr_t));
452 
453  } else if (vp->da == attr_tacacs_packet) {
454  fr_dcursor_t child_cursor;
455 
457  FR_PROTO_STACK_PRINT(&da_stack, 0);
458 
459  fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
460 
461  /*
462  * Call the struct encoder to do the actual work,
463  * which fills the struct fields with zero if the member VP is not used.
464  */
465  len = fr_struct_to_network(&work_dbuff, &da_stack, 0, &child_cursor, NULL, NULL, NULL);
466  if (len != sizeof(fr_tacacs_packet_hdr_t)) {
467  fr_strerror_printf("%s: Failed encoding %s using fr_struct_to_network()",
468  __FUNCTION__, attr_tacacs_packet->name);
469  return -1;
470  }
471  fr_dcursor_next(&cursor);
472 
473  } else {
475  FR_PROTO_STACK_PRINT(&da_stack, 0);
476 
477  /*
478  * Call the struct encoder to do the actual work,
479  * which fills the struct fields with zero if the member VP is not used.
480  */
481  len = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, NULL, NULL, NULL);
482  if (len != sizeof(fr_tacacs_packet_hdr_t)) {
483  fr_strerror_printf("%s: Failed encoding %s using fr_struct_to_network()",
484  __FUNCTION__, attr_tacacs_packet->name);
485  return -1;
486  }
487  }
488 
489  /*
490  * Ensure that we send a sane reply to a request.
491  */
492  if (original) {
493  packet->hdr.version = original->version;
494  packet->hdr.type = original->type;
495  packet->hdr.flags = original->flags; /* encrypted && single connection */
496  packet->hdr.session_id = original->session_id;
497 
498  /*
499  * The client may not set SINGLE_CONNECT flag. So if the administrator has set it in the reply,
500  * we allow setting the flag. This lets the server tell the client that it supports "single
501  * connection" mode.
502  */
504  if (vp) packet->hdr.flags |= (vp->vp_uint8 & FR_TAC_PLUS_SINGLE_CONNECT_FLAG);
505  }
506 
507  /*
508  * Starting here is a 'body' that may require encryption.
509  */
510  fr_dbuff_marker(&body, &work_dbuff);
511 
512  /*
513  * Encode 8 octets of various fields not members of STRUCT
514  */
515  switch (packet->hdr.type) {
516  case FR_TAC_PLUS_AUTHEN:
517  /*
518  * seq_no
519  *
520  * This is the sequence number of the current packet for the current session.
521  * The first packet in a session MUST have the sequence number 1 and each
522  * subsequent packet will increment the sequence number by one. Thus clients
523  * only send packets containing odd sequence numbers, and TACACS+ servers only
524  * send packets containing even sequence numbers.
525  */
526  if (packet_is_authen_start_request(packet)) { /* Start */
527  /**
528  * 4.1. The Authentication START Packet Body
529  *
530  * 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
531  * +----------------+----------------+----------------+----------------+
532  * | action | priv_lvl | authen_type | authen_service |
533  * +----------------+----------------+----------------+----------------+
534  * | user_len | port_len | rem_addr_len | data_len |
535  * +----------------+----------------+----------------+----------------+
536  * | user ...
537  * +----------------+----------------+----------------+----------------+
538  * | port ...
539  * +----------------+----------------+----------------+----------------+
540  * | rem_addr ...
541  * +----------------+----------------+----------------+----------------+
542  * | data...
543  * +----------------+----------------+----------------+----------------+
544  */
545 
546  /*
547  * Make room for such body request.
548  */
549  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_start));
550 
551  /*
552  * Encode 4 octets of various flags.
553  */
554  ENCODE_FIELD_UINT8(packet->authen_start.action, attr_tacacs_action);
555  ENCODE_FIELD_UINT8(packet->authen_start.priv_lvl, attr_tacacs_privilege_level);
556  ENCODE_FIELD_UINT8(packet->authen_start.authen_type, attr_tacacs_authentication_type);
557  ENCODE_FIELD_UINT8(packet->authen_start.authen_service, attr_tacacs_authentication_service);
558 
559  /*
560  * Encode 4 mandatory fields.
561  */
562  ENCODE_FIELD_STRING8(packet->authen_start.user_len, attr_tacacs_user_name);
563  ENCODE_FIELD_STRING8(packet->authen_start.port_len, attr_tacacs_client_port);
564  ENCODE_FIELD_STRING8(packet->authen_start.rem_addr_len, attr_tacacs_remote_address);
565 
566  /*
567  * No explicit "Data" attribute, try to automatically determine what to do.
568  */
570  ENCODE_FIELD_STRING8(packet->authen_start.data_len, attr_tacacs_data);
571 
572  } else switch (packet->authen_start.authen_type) {
573  default:
574  break;
575 
576  case FR_AUTHENTICATION_TYPE_VALUE_PAP:
577  ENCODE_FIELD_STRING8(packet->authen_start.data_len, attr_tacacs_user_password);
578  break;
579 
580  case FR_AUTHENTICATION_TYPE_VALUE_CHAP:
581  if (tacacs_encode_chap(&work_dbuff, packet, vps, attr_tacacs_chap_password, attr_tacacs_chap_challenge) < 0) return -1;
582  break;
583 
584  case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP: {
585  int rcode;
586 
588  if (rcode < 0) return rcode;
589 
590  if (rcode > 0) break;
591 
592  if (tacacs_encode_chap(&work_dbuff, packet, vps, attr_tacacs_mschap2_response, attr_tacacs_mschap_challenge) < 0) return -1;
593  }
594  break;
595  }
596 
597  goto check_request;
598 
599  } else if (packet_is_authen_continue(packet)) {
600  /*
601  * 4.3. The Authentication CONTINUE Packet Body
602  *
603  * This packet is sent from the client to the server following the receipt of
604  * a REPLY packet.
605  *
606  * 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
607  * +----------------+----------------+----------------+----------------+
608  * | user_msg len | data_len |
609  * +----------------+----------------+----------------+----------------+
610  * | flags | user_msg ...
611  * +----------------+----------------+----------------+----------------+
612  * | data ...
613  * +----------------+
614  */
615 
616  /*
617  * Make room for such body request.
618  */
619  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_cont));
620 
621  /*
622  * Encode 2 mandatory fields.
623  */
624  ENCODE_FIELD_STRING16(packet->authen_cont.user_msg_len, attr_tacacs_user_message);
625  ENCODE_FIELD_STRING16(packet->authen_cont.data_len, attr_tacacs_data);
626 
627  /*
628  * Look at the abort flag after encoding the fields.
629  */
631 
632  goto check_request;
633 
634  } else if (packet_is_authen_reply(packet)) {
635  /*
636  * 4.2. The Authentication REPLY Packet Body
637  *
638  * 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
639  * +----------------+----------------+----------------+----------------+
640  * | status | flags | server_msg_len |
641  * +----------------+----------------+----------------+----------------+
642  * | data_len | server_msg ...
643  * +----------------+----------------+----------------+----------------+
644  * | data ...
645  * +----------------+----------------+
646  */
647 
648  /*
649  * Make room for such body request.
650  */
651  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->authen_reply));
652 
653  ENCODE_FIELD_UINT8(packet->authen_reply.status, attr_tacacs_authentication_status);
654  ENCODE_FIELD_UINT8(packet->authen_reply.flags, attr_tacacs_authentication_flags);
655 
656  /*
657  * Encode 2 mandatory fields.
658  */
659  ENCODE_FIELD_STRING16(packet->authen_reply.server_msg_len, attr_tacacs_server_message);
660  ENCODE_FIELD_STRING16(packet->authen_reply.data_len, attr_tacacs_data);
661 
662  goto check_reply;
663  }
664 
665  fr_strerror_const("encode: Unknown authentication packet type");
666  return -1;
667 
668  case FR_TAC_PLUS_AUTHOR:
669  if (packet_is_author_request(packet)) {
670  /*
671  * 5.1. The Authorization REQUEST Packet Body
672  *
673  * 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
674  * +----------------+----------------+----------------+----------------+
675  * | authen_method | priv_lvl | authen_type | authen_service |
676  * +----------------+----------------+----------------+----------------+
677  * | user_len | port_len | rem_addr_len | arg_cnt |
678  * +----------------+----------------+----------------+----------------+
679  * | arg_1_len | arg_2_len | ... | arg_N_len |
680  * +----------------+----------------+----------------+----------------+
681  * | user ...
682  * +----------------+----------------+----------------+----------------+
683  * | port ...
684  * +----------------+----------------+----------------+----------------+
685  * | rem_addr ...
686  * +----------------+----------------+----------------+----------------+
687  * | arg_1 ...
688  * +----------------+----------------+----------------+----------------+
689  * | arg_2 ...
690  * +----------------+----------------+----------------+----------------+
691  * | ...
692  * +----------------+----------------+----------------+----------------+
693  * | arg_N ...
694  * +----------------+----------------+----------------+----------------+
695  */
696 
697  /*
698  * Make room for such body request.
699  */
700  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->author_req));
701 
702  /*
703  * Encode 4 octets of various flags.
704  */
705  ENCODE_FIELD_UINT8(packet->author_req.authen_method, attr_tacacs_authentication_method);
706  ENCODE_FIELD_UINT8(packet->author_req.priv_lvl, attr_tacacs_privilege_level);
707  ENCODE_FIELD_UINT8(packet->author_req.authen_type, attr_tacacs_authentication_type);
708  ENCODE_FIELD_UINT8(packet->author_req.authen_service, attr_tacacs_authentication_service);
709 
710  /*
711  * Encode 'arg_N' arguments (horrible format)
712  */
713  packet->author_req.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
714  if (packet->author_req.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->author_req.arg_cnt);
715 
716  /*
717  * Encode 3 mandatory fields.
718  */
719  ENCODE_FIELD_STRING8(packet->author_req.user_len, attr_tacacs_user_name);
720  ENCODE_FIELD_STRING8(packet->author_req.port_len, attr_tacacs_client_port);
721  ENCODE_FIELD_STRING8(packet->author_req.rem_addr_len, attr_tacacs_remote_address);
722 
723  /*
724  * Append 'args_body' to the end of buffer
725  */
726  if (packet->author_req.arg_cnt > 0) {
727  if (tacacs_encode_body_arg_n(&work_dbuff, packet->author_req.arg_cnt, &packet->author_req.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
728  }
729 
730  goto check_request;
731  } else if (packet_is_author_reply(packet)) {
732  /*
733  * 5.2. The Authorization RESPONSE Packet Body
734  *
735  * 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
736  * +----------------+----------------+----------------+----------------+
737  * | status | arg_cnt | server_msg len |
738  * +----------------+----------------+----------------+----------------+
739  * + data_len | arg_1_len | arg_2_len |
740  * +----------------+----------------+----------------+----------------+
741  * | ... | arg_N_len | server_msg ...
742  * +----------------+----------------+----------------+----------------+
743  * | data ...
744  * +----------------+----------------+----------------+----------------+
745  * | arg_1 ...
746  * +----------------+----------------+----------------+----------------+
747  * | arg_2 ...
748  * +----------------+----------------+----------------+----------------+
749  * | ...
750  * +----------------+----------------+----------------+----------------+
751  * | arg_N ...
752  * +----------------+----------------+----------------+----------------+
753  */
754 
755  /*
756  * Make room for such body request.
757  */
758  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->author_reply));
759 
760  /*
761  * Encode 1 mandatory field.
762  */
763  ENCODE_FIELD_UINT8(packet->author_reply.status, attr_tacacs_authorization_status);
764 
765  /*
766  * Encode 'arg_N' arguments (horrible format)
767  *
768  * For ERRORs, we don't encode arguments.
769  *
770  * 5.2
771  *
772  * A status of TAC_PLUS_AUTHOR_STATUS_ERROR indicates an error occurred
773  * on the server. For the differences between ERROR and FAIL, refer to
774  * section Session Completion (Section 3.4) . None of the arg values
775  * have any relevance if an ERROR is set, and must be ignored.
776  *
777  * When the status equals TAC_PLUS_AUTHOR_STATUS_FOLLOW, then the
778  * arg_cnt MUST be 0.
779  */
780  if (!((packet->author_reply.status == FR_AUTHORIZATION_STATUS_VALUE_ERROR) ||
781  (packet->author_reply.status == FR_AUTHORIZATION_STATUS_VALUE_FOLLOW))) {
782  packet->author_reply.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
783  if (packet->author_reply.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->author_reply.arg_cnt);
784  } else {
785  packet->author_reply.arg_cnt = 0;
786  }
787 
788  /*
789  * Encode 2 mandatory fields.
790  */
791  ENCODE_FIELD_STRING16(packet->author_reply.server_msg_len, attr_tacacs_server_message);
792  ENCODE_FIELD_STRING16(packet->author_reply.data_len, attr_tacacs_data);
793 
794  /*
795  * Append 'args_body' to the end of buffer
796  */
797  if (packet->author_reply.arg_cnt > 0) {
798  if (tacacs_encode_body_arg_n(&work_dbuff, packet->author_reply.arg_cnt, &packet->author_reply.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
799  }
800 
801  goto check_reply;
802 
803  }
804 
805  fr_strerror_const("encode: Unknown authorization packet type");
806  return -1;
807 
808  case FR_TAC_PLUS_ACCT:
809  if (packet_is_acct_request(packet)) {
810  /**
811  * 6.1. The Account REQUEST Packet Body
812  *
813  * 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
814  * +----------------+----------------+----------------+----------------+
815  * | flags | authen_method | priv_lvl | authen_type |
816  * +----------------+----------------+----------------+----------------+
817  * | authen_service | user_len | port_len | rem_addr_len |
818  * +----------------+----------------+----------------+----------------+
819  * | arg_cnt | arg_1_len | arg_2_len | ... |
820  * +----------------+----------------+----------------+----------------+
821  * | arg_N_len | user ...
822  * +----------------+----------------+----------------+----------------+
823  * | port ...
824  * +----------------+----------------+----------------+----------------+
825  * | rem_addr ...
826  * +----------------+----------------+----------------+----------------+
827  * | arg_1 ...
828  * +----------------+----------------+----------------+----------------+
829  * | arg_2 ...
830  * +----------------+----------------+----------------+----------------+
831  * | ...
832  * +----------------+----------------+----------------+----------------+
833  * | arg_N ...
834  * +----------------+----------------+----------------+----------------+
835  */
836 
837  /*
838  * Make room for such body request.
839  */
840  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->acct_req));
841 
842  /*
843  * Encode 5 octets of various flags.
844  */
845  ENCODE_FIELD_UINT8(packet->acct_req.flags, attr_tacacs_accounting_flags);
846  ENCODE_FIELD_UINT8(packet->acct_req.authen_method, attr_tacacs_authentication_method);
847  ENCODE_FIELD_UINT8(packet->acct_req.priv_lvl, attr_tacacs_privilege_level);
848  ENCODE_FIELD_UINT8(packet->acct_req.authen_type, attr_tacacs_authentication_type);
849  ENCODE_FIELD_UINT8(packet->acct_req.authen_service, attr_tacacs_authentication_service);
850 
851  /*
852  * Encode 'arg_N' arguments (horrible format)
853  */
854  packet->acct_req.arg_cnt = tacacs_encode_body_arg_cnt(vps, attr_tacacs_argument_list);
855  if (packet->acct_req.arg_cnt) FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, packet->acct_req.arg_cnt);
856 
857  /*
858  * Encode 3 mandatory fields.
859  */
860  ENCODE_FIELD_STRING8(packet->acct_req.user_len, attr_tacacs_user_name);
861  ENCODE_FIELD_STRING8(packet->acct_req.port_len, attr_tacacs_client_port);
862  ENCODE_FIELD_STRING8(packet->acct_req.rem_addr_len, attr_tacacs_remote_address);
863 
864  /*
865  * Append 'args_body' to the end of buffer
866  */
867  if (packet->acct_req.arg_cnt > 0) {
868  if (tacacs_encode_body_arg_n(&work_dbuff, packet->acct_req.arg_cnt, &packet->acct_req.arg_len[0], vps, attr_tacacs_argument_list) < 0) goto error;
869  }
870 
871  check_request:
872  /*
873  * Just to avoid malformed packet.
874  */
875  fr_dbuff_set(&hdr_io, &hdr);
876  fr_dbuff_out(&version_byte, &hdr_io);
877  if (!version_byte) {
878  version_byte = 0xc1; /* version 12.1 */
879  fr_dbuff_set(&hdr_io, &hdr);
880  FR_DBUFF_IN_RETURN(&hdr_io, version_byte);
881  }
882  /*
883  * If the caller didn't set a session ID, use a random one.
884  */
886  packet->hdr.session_id = fr_rand();
887  }
888 
889  /*
890  * Requests have odd sequence numbers.
891  */
892  packet->hdr.seq_no |= 0x01;
893  break;
894 
895  } else if (packet_is_acct_reply(packet)) {
896  /**
897  * 6.2. The Accounting REPLY 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  * | server_msg len | data_len |
902  * +----------------+----------------+----------------+----------------+
903  * | status | server_msg ...
904  * +----------------+----------------+----------------+----------------+
905  * | data ...
906  * +----------------+
907  */
908 
909  /*
910  * Make room for such body request.
911  */
912  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, sizeof(packet->acct_reply));
913 
914  /*
915  * Encode 2 mandatory fields.
916  */
917  ENCODE_FIELD_STRING16(packet->acct_reply.server_msg_len, attr_tacacs_server_message);
918  ENCODE_FIELD_STRING16(packet->acct_reply.data_len, attr_tacacs_data);
919 
920  /*
921  * And also, the status field.
922  */
923  ENCODE_FIELD_UINT8(packet->acct_reply.status, attr_tacacs_accounting_status);
924 
925  check_reply:
926  /*
927  * fr_struct_to_network() fills the struct fields with 0
928  * if there is no matching VP. In the interest of making
929  * things easier for the user, we don't require them to
930  * copy all of the fields from the request to the reply.
931  *
932  * Instead, we copy the fields manually, and ensure that
933  * they have the correct values.
934  */
935  if (original) {
936  fr_dbuff_set(&hdr_io, &hdr);
937  fr_dbuff_out(&version_byte, &hdr_io);
938  if (!version_byte) {
939  packet->hdr.version = original->version;
940  }
941 
942  if (!packet->hdr.seq_no) {
943  packet->hdr.seq_no = original->seq_no + 1; /* uint8_t */
944  }
945 
946  if (!packet->hdr.session_id) {
947  packet->hdr.session_id = original->session_id;
948  }
949  }
950 
951  /*
952  * Replies have even sequence numbers.
953  */
954  packet->hdr.seq_no &= 0xfe;
955  break;
956  }
957 
958  fr_strerror_const("encode: Unknown accounting packet type");
959  return -1;
960 
961  default:
962  fr_strerror_printf("encode: unknown packet type %u", packet->hdr.type);
963  return -1;
964  }
965 
966  /*
967  * Force the correct header type, and randomly-placed
968  * status fields. But only if there's no code field.
969  * Only the unit tests pass a zero code field, as that's
970  * normally invalid. The unit tests ensure that all of
971  * the VPs are passed to encode a packet, and they all
972  * must be correct
973  */
974  if (code && (fr_tacacs_code_to_packet(packet, code) < 0)) return -1;
975 
976  /*
977  * The packet length we store in the header doesn't
978  * include the size of the header. But we tell the
979  * caller about the total length of the packet.
980  */
981  packet_len = fr_dbuff_used(&work_dbuff);
982  fr_assert(packet_len >= sizeof(fr_tacacs_packet_hdr_t));
983 
984  body_len = (packet_len - sizeof(fr_tacacs_packet_hdr_t));
985  fr_assert(packet_len < FR_MAX_PACKET_SIZE);
986  packet->hdr.length = htonl(body_len);
987 
988  /*
989  * If the original packet is encrypted, then the reply
990  * MUST be encrypted too.
991  *
992  * On the other hand, if the request is unencrypted,
993  * we're OK with sending an encrypted reply. Because,
994  * whatever.
995  */
996  if (original &&
997  ((original->flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0)) {
999  }
1000 
1001 #ifndef NDEBUG
1002  if (fr_debug_lvl >= L_DBG_LVL_4) {
1003  uint8_t flags = packet->hdr.flags;
1004 
1006  fr_tacacs_packet_log_hex(&default_log, packet, packet_len);
1007  packet->hdr.flags = flags;
1008  }
1009 #endif
1010 
1011  /*
1012  * 3.6. Encryption
1013  *
1014  * Packets are encrypted if the unencrypted flag is clear.
1015  */
1016  if (secret) {
1017  if (!secret_len) {
1018  fr_strerror_const("Packet should be decrypted, but the secret has zero length");
1019  return -1;
1020  }
1021 
1022  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (unencrypted)");
1023 
1024  if (fr_tacacs_body_xor(packet, fr_dbuff_current(&body), body_len, secret, secret_len) != 0) return -1;
1025 
1027  } else {
1028  /*
1029  * Packets which have no secret cannot be encrypted.
1030  */
1032 
1033  }
1034 
1035  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (encoded)");
1036 
1037  return fr_dbuff_set(dbuff, &work_dbuff);
1038 }
1039 
1040 /*
1041  * Test points for protocol encode
1042  */
1044  uint8_t *data, size_t data_len, void *proto_ctx)
1045 {
1046  fr_tacacs_ctx_t *test_ctx = talloc_get_type_abort(proto_ctx, fr_tacacs_ctx_t);
1047 
1048  return fr_tacacs_encode(&FR_DBUFF_TMP(data, data_len), NULL, test_ctx->secret, (talloc_array_length(test_ctx->secret)-1), 0, vps);
1049 }
1050 
1051 static int _encode_test_ctx(fr_tacacs_ctx_t *proto_ctx)
1052 {
1053  talloc_const_free(proto_ctx->secret);
1054 
1056 
1057  return 0;
1058 }
1059 
1060 static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
1061 {
1062  fr_tacacs_ctx_t *test_ctx;
1063 
1064  if (fr_tacacs_global_init() < 0) return -1;
1065 
1066  test_ctx = talloc_zero(ctx, fr_tacacs_ctx_t);
1067  if (!test_ctx) return -1;
1068 
1069  test_ctx->root = fr_dict_root(dict_tacacs);
1070  talloc_set_destructor(test_ctx, _encode_test_ctx);
1071 
1072  *out = test_ctx;
1073 
1074  return 0;
1075 }
1076 
1077 /*
1078  * Test points
1079  */
1083  .func = fr_tacacs_encode_proto
1084 };
static int const char char buffer[256]
Definition: acutest.h:574
#define UNUSED
Definition: build.h:313
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition: dbuff.h:762
#define FR_DBUFF_ADVANCE_RETURN(_dbuff_or_marker, _len)
Advance the 'current' position in dbuff or marker by _len bytes returning if _len is out of range.
Definition: dbuff.h:1083
struct fr_dbuff_marker_s fr_dbuff_marker_t
A position marker associated with a dbuff.
Definition: dbuff.h:81
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:906
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition: dbuff.h:893
#define FR_DBUFF_MEMSET_RETURN(_dbuff_or_marker, _c, _inlen)
Set _inlen bytes of a dbuff or marker to _c returning if there is insufficient space.
Definition: dbuff.h:1503
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1377
#define FR_DBUFF_REMAINING_RETURN(_dbuff_or_marker, _len)
Check if _len bytes are available in the dbuff and if not return the number of bytes we'd need.
Definition: dbuff.h:756
#define FR_DBUFF_IN_RETURN(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff returning if there is insufficient space.
Definition: dbuff.h:1580
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:222
#define FR_DBUFF_MAX(_dbuff_or_marker, _max)
Limit the maximum number of bytes available in the dbuff when passing it to another function.
Definition: dbuff.h:301
static uint8_t * fr_dbuff_marker(fr_dbuff_marker_t *m, fr_dbuff_t *dbuff)
Initialises a new marker pointing to the 'current' position of the dbuff.
Definition: dbuff.h:1187
#define fr_dbuff_out(_out, _dbuff_or_marker)
Copy data from a dbuff or marker to a fixed sized C type.
Definition: dbuff.h:1779
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1467
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
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_t const * fr_dict_by_da(fr_dict_attr_t const *da)
Attempt to locate the protocol dictionary containing an attribute.
Definition: dict_util.c:2203
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
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
Definition: merged_model.c:113
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
Definition: merged_model.c:122
@ FR_TYPE_UINT64
64 Bit unsigned integer.
Definition: merged_model.c:100
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
#define UINT8_MAX
Definition: merged_model.c:32
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_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
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_session_id
Definition: base.c:71
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
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
Build a complete DA stack from the da back to the root.
Definition: proto.c:118
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_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
HIDDEN fr_dict_attr_t const * attr_tacacs_flags
Definition: base.c:57
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 ssize_t tacacs_encode_chap(fr_dbuff_t *dbuff, fr_tacacs_packet_t *packet, fr_pair_list_t *vps, fr_dict_attr_t const *da_chap, fr_dict_attr_t const *da_challenge)
Definition: encode.c:310
int fr_tacacs_code_to_packet(fr_tacacs_packet_t *pkt, uint32_t code)
Definition: encode.c:36
static ssize_t tacacs_encode_field(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_dict_attr_t const *da, size_t max_len)
Definition: encode.c:291
#define ENCODE_FIELD_UINT8(_field, _da)
Definition: encode.c:352
ssize_t fr_tacacs_encode(fr_dbuff_t *dbuff, uint8_t const *original_packet, char const *secret, size_t secret_len, unsigned int code, fr_pair_list_t *vps)
Encode VPS into a raw TACACS packet.
Definition: encode.c:363
static uint8_t tacacs_encode_body_arg_cnt(fr_pair_list_t *vps, fr_dict_attr_t const *da)
Encode a TACACS+ 'arg_N' fields.
Definition: encode.c:133
static ssize_t fr_tacacs_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, void *proto_ctx)
Definition: encode.c:1043
static int _encode_test_ctx(fr_tacacs_ctx_t *proto_ctx)
Definition: encode.c:1051
#define ENCODE_FIELD_STRING8(_field, _da)
Definition: encode.c:357
fr_test_point_proto_encode_t tacacs_tp_encode_proto
Definition: encode.c:1081
#define ENCODE_FIELD_STRING16(_field, _da)
Definition: encode.c:358
static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint8_t *arg_len, fr_pair_list_t *vps, fr_dict_attr_t const *da)
Definition: encode.c:182
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
Definition: encode.c:1060
VQP attributes.
static char * secret
Definition: radclient-ng.c:69
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition: sbuff.c:1554
#define FR_SBUFF_OUT(_start, _len_or_end)
fr_assert(0)
fr_pair_t * vp
ssize_t fr_struct_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *parent_cursor, void *encode_ctx, fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair)
Definition: struct.c:478
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
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_TAC_PLUS_CONTINUE_FLAG_UNSET
Definition: tacacs.h:174
@ FR_TAC_PLUS_CONTINUE_FLAG_ABORT
Definition: tacacs.h:175
@ FR_TAC_PLUS_SINGLE_CONNECT_FLAG
Definition: tacacs.h:80
@ 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_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_flags_t flags
Definition: tacacs.h:99
fr_tacacs_type_t type
Definition: tacacs.h:97
uint32_t session_id
Definition: tacacs.h:100
#define FR_MAX_PACKET_SIZE
Definition: tacacs.h:27
@ 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:74
Entry point for protocol encoders.
Definition: test_point.h:73
@ T_BARE_WORD
Definition: token.h:120
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition: pair_print.c:40
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:590
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
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition: proto.h:41
#define FR_PROTO_TRACE(_fmt,...)
Definition: proto.h:40
#define FR_PROTO_STACK_PRINT(_stack, _depth)
Definition: proto.h:43
Structure for holding the stack of dictionary attributes being encoded.
Definition: proto.h:54
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition: value.c:3301
return fr_dbuff_set(dbuff, &our_dbuff)
static fr_slen_t data
Definition: value.h:1259
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:574
static size_t char ** out
Definition: value.h:984