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