The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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: b601b19bd593f201a1ce7d12418b33b0b8541174 $
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:
105 pkt->author_reply.status = FR_TAC_PLUS_AUTHOR_STATUS_FAIL;
106 break;
107
108 case FR_PACKET_TYPE_VALUE_ACCOUNTING_REQUEST:
110 break;
111
112 case FR_PACKET_TYPE_VALUE_ACCOUNTING_SUCCESS:
114 pkt->acct_reply.status = FR_TAC_PLUS_ACCT_STATUS_SUCCESS;
115 break;
116
117 case FR_PACKET_TYPE_VALUE_ACCOUNTING_ERROR:
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
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:
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:
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 */
291static 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
310static 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 */
363ssize_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 */
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) {
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
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 %d", 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)) {
998 packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
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
1026 packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
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
1051static 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
1060static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
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 */
static int const char char buffer[256]
Definition acutest.h:576
#define UNUSED
Definition build.h:315
#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:767
#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:1088
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:911
#define fr_dbuff_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition dbuff.h:898
#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:1508
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:1192
#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:1382
#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:761
#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:1585
#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
#define fr_dbuff_out(_out, _dbuff_or_marker)
Copy data from a dbuff or marker to a fixed sized C type.
Definition dbuff.h:1799
#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:1472
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
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:2606
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
int fr_debug_lvl
Definition log.c:43
fr_log_t default_log
Definition log.c:291
@ L_DBG_LVL_4
4th highest priority debug messages (-xxxx | -Xxx).
Definition log.h:73
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
#define UINT8_MAX
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:770
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:693
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
static fr_dict_attr_t const * attr_tacacs_user_name
static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
Definition encode.c:165
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
#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(UNUSED fr_tftp_ctx_t *proto_ctx)
Definition encode.c:250
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
static char * secret
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1595
#define FR_SBUFF_OUT(_start, _len_or_end)
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:470
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:331
#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:355
fr_dict_attr_t const * root
Definition tacacs.h:330
@ FR_TAC_PLUS_AUTHOR_STATUS_PASS_ADD
Definition tacacs.h:211
@ FR_TAC_PLUS_AUTHOR_STATUS_FAIL
Definition tacacs.h:213
@ FR_TAC_PLUS_AUTHOR_STATUS_PASS_REPL
Definition tacacs.h:212
#define packet_is_acct_reply(p)
Definition tacacs.h:57
@ FR_TAC_PLUS_ACCT
Definition tacacs.h:67
@ FR_TAC_PLUS_AUTHEN
Definition tacacs.h:65
@ FR_TAC_PLUS_AUTHOR
Definition tacacs.h:66
#define packet_is_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
#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:329
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:75
Entry point for protocol encoders.
Definition test_point.h:74
@ T_BARE_WORD
Definition token.h:120
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:53
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:591
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
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.
#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:3352
static fr_slen_t data
Definition value.h:1265
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:587
static size_t char ** out
Definition value.h:997