The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; 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: df0cfddfa9443f14d974d79f8870365e41f1a7a3 $
19 * @file src/process/tacacs/base.c
20 * @brief TACACS+ handler.
21 * @author Jorge Pereira <jpereira@freeradius.org>
22 *
23 * @copyright 2020 The FreeRADIUS server project.
24 * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
25 */
26#include <freeradius-devel/io/listen.h>
27#include <freeradius-devel/io/master.h>
28#include <freeradius-devel/server/main_config.h>
29#include <freeradius-devel/server/protocol.h>
30#include <freeradius-devel/server/state.h>
31#include <freeradius-devel/server/rcode.h>
32#include <freeradius-devel/tacacs/tacacs.h>
33#include <freeradius-devel/unlang/call.h>
34#include <freeradius-devel/util/debug.h>
35
36#include <freeradius-devel/protocol/tacacs/tacacs.h>
37
39static fr_dict_t const *dict_tacacs;
40
43 { .out = &dict_freeradius, .proto = "freeradius" },
44 { .out = &dict_tacacs, .proto = "tacacs" },
45 { NULL }
46};
47
53
60
64
74
78
81 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
82 { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
83 { .out = &attr_module_success_message, .name = "Module-Success-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
84 { .out = &attr_stripped_user_name, .name = "Stripped-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
85 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
86
87 { .out = &attr_tacacs_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
88 { .out = &attr_tacacs_authentication_flags, .name = "Authentication-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
89 { .out = &attr_tacacs_authentication_type, .name = "Authentication-Type", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
90 { .out = &attr_tacacs_authentication_service, .name = "Authentication-Service", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
91
92 { .out = &attr_tacacs_authentication_status, .name = "Authentication-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
93 { .out = &attr_tacacs_authorization_status, .name = "Authorization-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
94
95 { .out = &attr_tacacs_accounting_status, .name = "Accounting-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
96 { .out = &attr_tacacs_accounting_flags, .name = "Accounting-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
97
98 { .out = &attr_tacacs_client_port, .name = "Client-Port", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
99 { .out = &attr_tacacs_data, .name = "Data", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
100 { .out = &attr_tacacs_privilege_level, .name = "Privilege-Level", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
101 { .out = &attr_tacacs_remote_address, .name = "Remote-Address", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
102 { .out = &attr_tacacs_authentication_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
103 { .out = &attr_tacacs_session_id, .name = "Packet.Session-Id", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
104 { .out = &attr_tacacs_sequence_number, .name = "Packet.Sequence-Number", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
105 { .out = &attr_tacacs_server_message, .name = "Server-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
106 { .out = &attr_tacacs_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
107 { .out = &attr_tacacs_user_message, .name = "User-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
108
109 { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
110 { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
111 { .out = &attr_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
112
113 { NULL }
114};
115
120
123 { .out = &enum_auth_type_accept, .name = "Accept", .attr = &attr_auth_type },
124 { .out = &enum_auth_type_reject, .name = "Reject", .attr = &attr_auth_type },
125 { .out = &enum_auth_flags_noecho, .name = "No-Echo", .attr = &attr_tacacs_authentication_flags },
126 { .out = &enum_tacacs_auth_type_ascii, .name = "ASCII", .attr = &attr_tacacs_authentication_type },
127 { NULL }
128};
129
130
162
163typedef struct {
164 fr_time_delta_t session_timeout; //!< Maximum time between the last response and next request.
165 uint32_t max_session; //!< Maximum ongoing session allowed.
166
167 uint32_t max_rounds; //!< maximum number of authentication rounds allowed
168
169 uint8_t state_server_id; //!< Sets a specific byte in the state to allow the
170 //!< authenticating server to be identified in packet
171 //!<captures.
172
173 fr_state_tree_t *state_tree; //!< State tree to link multiple requests/responses.
175
176typedef struct {
177 CONF_SECTION *server_cs; //!< Our virtual server.
178
179 uint32_t session_id; //!< current session ID
180
181 process_tacacs_sections_t sections; //!< Pointers to various config sections
182 ///< we need to execute
183
184 process_tacacs_auth_t auth; //!< Authentication configuration.
185
186
188
189typedef struct {
190 uint32_t rounds; //!< how many rounds were taken
191 uint32_t reply; //!< for multiround state machine
192 uint8_t seq_no; //!< sequence number of last request.
193 fr_pair_list_t list; //!< copied from the request
195
196
197#define PROCESS_PACKET_TYPE fr_tacacs_packet_code_t
198#define PROCESS_CODE_MAX FR_TACACS_CODE_MAX
199#define PROCESS_CODE_DO_NOT_RESPOND FR_TACACS_CODE_DO_NOT_RESPOND
200#define PROCESS_PACKET_CODE_VALID FR_TACACS_PACKET_CODE_VALID
201#define PROCESS_INST process_tacacs_t
202#define PROCESS_CODE_DYNAMIC_CLIENT FR_TACACS_CODE_AUTH_PASS
203
204#include <freeradius-devel/server/process.h>
205
207 { FR_CONF_OFFSET("timeout", process_tacacs_auth_t, session_timeout), .dflt = "15" },
208 { FR_CONF_OFFSET("max", process_tacacs_auth_t, max_session), .dflt = "4096" },
209 { FR_CONF_OFFSET("max_rounds", process_tacacs_auth_t, max_rounds), .dflt = "4" },
210 { FR_CONF_OFFSET("state_server_id", process_tacacs_auth_t, state_server_id) },
211
213};
214
215static const conf_parser_t auth_config[] = {
216 { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) session_config },
217
219};
220
221static const conf_parser_t config[] = {
222 { FR_CONF_POINTER("Authentication", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) auth_config,
223 .offset = offsetof(process_tacacs_t, auth), },
224
226};
227
228
229/*
230 * Synthesize a State attribute from connection && session information.
231 */
232static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
233{
234 uint8_t buffer[12];
236 fr_pair_t *vp;
237
238 vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_session_id);
239 if (!vp) return -1;
240
241 fr_nbo_from_uint32(buffer, vp->vp_uint32);
242
243 vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_sequence_number);
244 if (!vp) return -1;
245
246 /*
247 * Requests have odd sequence numbers, and replies have even sequence numbers.
248 * So if we want to synthesize a state in a reply which gets matched with the next
249 * request, we have to add 2 to it.
250 */
251 hash = vp->vp_uint8 + ((int) reply << 1);
252
254
255 /*
256 * Hash in the listener. For now, we don't allow internally proxied requests.
257 */
258 fr_assert(request->async != NULL);
259 fr_assert(request->async->listen != NULL);
260 hash = fr_hash(&request->async->listen, sizeof(request->async->listen));
261
263
265 if (!vp) return -1;
266
267 (void) fr_pair_value_memdup(vp, buffer, 12, false);
268
270
271 return 0;
272}
273
274/** Try and determine what the response packet type should be
275 *
276 * We check three sources:
277 * - reply.``<status_attr>``
278 * - reply.Packet-Type
279 * - State machine packet type assignments for the section rcode
280 *
281 * @param[in] request The current request.
282 * @param[in] status_da Specialised status attribute.
283 * @param[in] status2code Mapping table of *packet* status types to rcodes.
284 * @param[in] state Mappings for process state machine
285 * @param[in] process_rcode Mappings for Auth-Type / Acct-Type, which don't use the process state machine
286 * @param[in] rcode The last section rcode.
287 * @return
288 * - >0 if we determined a reply code.
289 * - 0 if we couldn't - Usually indicates additional sections should be run.
290 */
291static uint32_t reply_code(request_t *request, fr_dict_attr_t const *status_da,
292 uint32_t const status2code[static UINT8_MAX + 1],
293 fr_process_state_t const *state, fr_process_rcode_t const process_rcode, rlm_rcode_t rcode)
294{
295 fr_pair_t *vp;
296 uint32_t code;
297
298 /*
299 * First check the protocol attribute for this packet type.
300 *
301 * Should be one of:
302 * - Authentication-Status
303 * - Authorization-Status
304 * - Accounting-Status
305 */
306 fr_assert(status_da->type == FR_TYPE_UINT8);
307
308 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, status_da);
309 if (vp) {
310 code = status2code[vp->vp_uint8];
311 if (FR_TACACS_PACKET_CODE_VALID(code)) {
312 RDEBUG("Setting reply Packet-Type from %pP", vp);
313 return code;
314 }
315 REDEBUG("Ignoring invalid status %pP", vp);
316 }
317
318 if (state) {
319 code = state->packet_type[rcode];
320 if (FR_TACACS_PACKET_CODE_VALID(code)) return code;
321 }
322
323 if (process_rcode) {
324 code = process_rcode[rcode];
325 if (FR_TACACS_PACKET_CODE_VALID(code)) return code;
326 }
327
328 /*
329 * Otherwise use Packet-Type (if set)
330 */
331 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
332 if (vp && FR_TACACS_PACKET_CODE_VALID(vp->vp_uint32)) {
333 RDEBUG("Setting reply Packet-Type from %pV", &vp->data);
334 return vp->vp_uint32;
335 }
336
337 return 0;
338}
339
340RECV(auth_start)
341{
342 fr_process_state_t const *state;
343 fr_pair_t *vp;
344
345 /*
346 * Only "Login" is supported. The others are "change password" and "sendauth", which aren't
347 * used.
348 */
349 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_action);
350 if (!vp) {
351 fail:
352 request->reply->code = FR_TACACS_CODE_AUTH_ERROR;
353 UPDATE_STATE(reply);
354
355 fr_assert(state->send != NULL);
356 return CALL_SEND_STATE(state);
357 }
358
359 if (vp->vp_uint8 != FR_ACTION_VALUE_LOGIN) {
360 RDEBUG("Invalid authentication action %u", vp->vp_uint8);
361 goto fail;
362 }
363
364 /*
365 * There is no state to restore, so we just run the section as normal.
366 */
367
368 return CALL_RECV(generic);
369}
370
371RESUME(auth_type);
372
382
383RESUME(auth_start)
384{
385 rlm_rcode_t rcode = RESULT_RCODE;
386 fr_pair_t *vp;
387 CONF_SECTION *cs;
388 fr_dict_enum_value_t const *dv;
389 fr_process_state_t const *state;
391
393
395
396 /*
397 * See if the return code from "recv" which says we reject, or continue.
398 */
399 UPDATE_STATE(packet);
400
401 /*
402 * Nothing set the reply, so let's see if we need to do so.
403 *
404 * If the admin didn't set authentication-status, just
405 * use the defaults from the state machine.
406 */
407 if (!request->reply->code) {
408 request->reply->code = reply_code(request,
410 authen_status_to_packet_code, state, NULL, rcode);
411 } else {
412 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
413 }
414
415 /*
416 * Check for multi-round authentication.
417 *
418 * We only run the automatic state machine (start -> getuser -> getpass -> pass/fail)
419 * when the admin does NOT set any reply type, or any reply authentication status.
420 *
421 * However, do DO always save and restore the attributes from the start packet, so that they are
422 * visible in a later packet.
423 */
424 if (!request->reply->code) {
426 fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
427
428 session = request_data_reference(request, inst, 0);
429 if (!session) {
430 /*
431 * This function is called for resuming both "start" and "continue" packets, so
432 * we have to check for "start" here.
433 *
434 * We only do multi-round authentication for the ASCII authentication type.
435 * Other authentication types are defined to be one request/reply only.
436 */
437 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
438 if (!packet_is_authen_start_request(packet) ||
439 (vp && (fr_value_box_cmp(&vp->data, enum_tacacs_auth_type_ascii) != 0))) {
440 goto auth_type;
441 }
442
443 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
444 if (!vp) {
445 RDEBUG("No User-Name, replying with Authentication-GetUser");
446 request->reply->code = FR_TACACS_CODE_AUTH_GETUSER;
447 } else {
448 RDEBUG("User-Name = %pV, replying with Authentication-GetPass", &vp->data);
449 request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
450 goto add_auth_flags;
451 }
452
453 goto send_reply;
454 }
455
456 /*
457 * Last reply was "get username", we now get the password.
458 */
459 if (session->reply == FR_TACACS_CODE_AUTH_GETUSER) {
460 RDEBUG("No User-Password, replying with Authentication-GetPass");
461 request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
462
463 /*
464 * Pre-set the authentication flags reply to No-Echo
465 * RFC 8907 says this should be set when the data being
466 * requested is sensitive and should not be echoed to the
467 * user as it is being entered.
468 */
469 add_auth_flags:
472 RPEDEBUG("Failed creating Authentication-Flags attribute with No-Echo flag");
474 goto reject;
475 }
477 goto send_reply;
478 }
479
480 /*
481 * We either have a password, or the admin screwed up the configuration somehow. Just go
482 * run "Auth-Type foo".
483 */
484 goto auth_type;
485 }
486
487 /*
488 * Something set the reply code, skip
489 * the normal auth flow and respond immediately.
490 */
491 if (request->reply->code) {
492 switch (request->reply->code) {
494 RDEBUG("The 'recv Authentication-Start' section returned %s - rejecting the request",
495 fr_table_str_by_value(rcode_table, rcode, "<INVALID>"));
496 break;
497
498 default:
499 RDEBUG("Reply packet type was set to %s", fr_tacacs_packet_names[request->reply->code]);
500 break;
501 }
502
504 UPDATE_STATE(reply);
505
506 fr_assert(state->send != NULL);
507 return CALL_SEND_STATE(state);
508 }
509
510 /*
511 * Run authenticate foo { ... }
512 *
513 * If we can't find Auth-Type, OR if we can't find Auth-Type = foo, then it's a reject.
514 *
515 * We prefer the local Auth-Type to the Authentication-Type in the packet. But if there's no
516 * Auth-Type set by the admin, then we use what's in the packet.
517 */
518 auth_type:
519 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_auth_type);
520 if (!vp) vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
521 if (!vp) {
522 RDEBUG("No 'Auth-Type' or 'Authentication-Type' attribute found, "
523 "cannot authenticate the user - rejecting the request");
524
525 reject:
526 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
527 goto send_reply;
528 }
529
530 dv = fr_dict_enum_by_value(vp->da, &vp->data);
531 if (!dv) {
532 RDEBUG("Invalid value for '%s' attribute, cannot authenticate the user - rejecting the request",
533 vp->da->name);
534
535 goto reject;
536 }
537
538 /*
539 * The magic Auth-Type Accept value which means skip the authenticate section.
540 *
541 * And Reject means always reject. Tho the admin should just return "reject" from the section.
542 */
543 if (vp->da == attr_auth_type) {
545 request->reply->code = FR_TACACS_CODE_AUTH_PASS;
546 goto send_reply;
547
548 } else if (fr_value_box_cmp(enum_auth_type_reject, dv->value) == 0) {
549 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
550 goto send_reply;
551 }
552 }
553
554 cs = cf_section_find(inst->server_cs, "authenticate", dv->name);
555 if (!cs) {
556 RDEBUG2("No 'authenticate %s { ... }' section found - rejecting the request", dv->name);
557 goto reject;
558 }
559
560 /*
561 * Run the "authenticate foo { ... }" section.
562 *
563 * And continue with sending the generic reply.
564 */
565 RDEBUG("Running 'authenticate %s' from file %s", cf_section_name2(cs), cf_filename(cs));
566 return unlang_module_yield_to_section(RESULT_P, request,
567 cs, RLM_MODULE_NOOP, resume_auth_type,
568 NULL, 0, mctx->rctx);
569}
570
571RESUME(auth_type)
572{
573 static const fr_process_rcode_t auth_type_rcode = {
582 };
583
584 rlm_rcode_t rcode = RESULT_RCODE;
585 fr_process_state_t const *state;
586 fr_pair_t *vp;
587
589
591
592 /*
593 * If nothing set the reply code, then try to set it from various other things.
594 *
595 * The user could have set Authentication-Status
596 * or Packet-Type to something other than
597 * pass...
598 */
599 if (!request->reply->code) {
600 request->reply->code = reply_code(request,
602 authen_status_to_packet_code, NULL, auth_type_rcode, rcode);
603 } else {
604 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
605 }
606
607 switch (request->reply->code) {
608 case 0:
609 RDEBUG("No reply code was set. Forcing to Authentication-Fail");
610 fail:
611 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
613
614 /*
615 * Print complaints before running "send Access-Reject"
616 */
618 RDEBUG2("Failed to authenticate the user");
619 break;
620
624 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
625 if (vp && (vp->vp_uint32 != FR_AUTHENTICATION_TYPE_VALUE_ASCII)) {
626 RDEBUG2("Cannot send challenges for %pP", vp);
627 goto fail;
628 }
629 break;
630
631 default:
632 break;
633
634 }
635 UPDATE_STATE(reply);
636
637 fr_assert(state->send != NULL);
638 return state->send(p_result, mctx, request);
639}
640
641RESUME_FLAG(auth_pass, UNUSED,)
642{
644
646
647 // @todo - worry about user identity existing?
648
649 fr_state_discard(inst->auth.state_tree, request);
651}
652
653RESUME_FLAG(auth_fail, UNUSED,)
654{
656
658
659 // @todo - insert server message saying "failed"
660 // and also for FAIL
661
662 fr_state_discard(inst->auth.state_tree, request);
664}
665
666RESUME_FLAG(auth_restart, UNUSED,)
667{
669
671
672 fr_state_discard(inst->auth.state_tree, request);
674}
675
676RESUME(auth_get)
677{
680 fr_pair_t *vp, *copy;
681
683
684 /*
685 * Track multi-round authentication flows. Note that they can only start with an
686 * "Authentication-Start" packet, but they can continue with an "Authentication-Continue" packet.
687 *
688 * If there's no session being tracked, then we create one for a start packet.
689 */
690 session = request_data_reference(request, inst, 0);
691 if (!session) {
692 fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
693
695
696 MEM(session = talloc_zero(NULL, process_tacacs_session_t));
697 if (request_data_talloc_add(request, inst, 0, process_tacacs_session_t, session, true, true, true) < 0) {
698 talloc_free(session);
699 goto send_reply;
700 }
701
702 /*
703 * These are the only things which saved. The rest of the fields are either static (and statically
704 * known), or are irrelevant.
705 */
706 fr_pair_list_init(&session->list);
707#undef COPY
708#define COPY(_attr) do { \
709 vp = fr_pair_find_by_da(&request->request_pairs, NULL, _attr); \
710 if (!vp) break; \
711 MEM(copy = fr_pair_copy(session, vp)); \
712 fr_pair_append(&session->list, copy); \
713 RDEBUG2("%pP", copy); \
714} while (0)
715
716 RDEBUG2("Caching session attributes:");
717 RINDENT();
723 REXDENT();
724
725 } else {
726 session->rounds++;
727
728 if (session->rounds > inst->auth.max_rounds) {
729 REDEBUG("Too many rounds of authentication - failing the session");
730 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_FAIL);
731 }
732
733 /*
734 * It is possible that the user name or password are added on subsequent Authentication-Continue
735 * packets following replies with Authentication-GetUser or Authentication-GetPass.
736 * Check if they are already in the session cache, and if not, add them.
737 */
738#define COPY_MISSING(_attr) do { \
739 vp = fr_pair_find_by_da(&session->list, NULL, _attr); \
740 if (vp) break; \
741 COPY(_attr); \
742} while (0)
743
744 RDEBUG2("Caching additional session attributes:");
745 RINDENT();
748 REXDENT();
749 }
750 session->reply = request->reply->code;
751 session->seq_no = request->packet->data[2];
752
754 /*
755 * Cache the session state context.
756 */
757 if ((state_create(request->reply_ctx, &request->reply_pairs, request, true) < 0) ||
758 (fr_request_to_state(inst->auth.state_tree, request) < 0)) {
759 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
760 }
761
763}
764
765RECV(auth_cont)
766{
769
770 if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
771 (fr_state_to_request(inst->auth.state_tree, request) < 0)) {
772 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
773 }
774
775 /*
776 * Restore key fields from the original Authentication-Start packet.
777 */
778 session = request_data_reference(request, inst, 0);
779 if (session) {
780 fr_pair_t *vp = NULL, *copy;
781
782 if (request->packet->data[2] <= session->seq_no) {
783 REDEBUG("Client sent invalid sequence number %02x, expected >%02x", request->packet->data[2], session->seq_no);
784 error:
785 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
786 }
787
788 if (fr_debug_lvl >= L_DBG_LVL_2) {
789 RDEBUG2("Restoring session attributes:");
790 RINDENT();
791 while ((vp = fr_pair_list_next(&session->list, vp))) {
792 RDEBUG2("%pP", vp);
793 }
794 REXDENT();
795 }
796 if (fr_pair_list_copy(request->request_ctx, &request->request_pairs, &session->list) < 0) goto error;
797
798 /*
799 * Copy the returned user_message into the attribute we requested.
800 */
801#define EXTRACT(_attr) \
802 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_user_message); \
803 if (!vp) break; \
804 fr_value_box_set_secret(&vp->data, _attr->flags.secret); \
805 if (pair_append_request(&copy, _attr) < 0) break; \
806 if (fr_pair_value_copy(copy, vp) < 0) { \
807 fr_pair_remove(&request->request_pairs, copy); \
808 talloc_free(copy); \
809 break; \
810 } \
811 RDEBUG2("Populated %pP from user_message", copy)
812
813 switch (session->reply) {
816 break;
817
820 break;
821
822 default:
823 break;
824 }
825 }
826
827 return CALL_RECV(generic);
828}
829
830/*
831 * The client aborted the session. The reply should be RESTART or FAIL.
832 */
833RECV(auth_cont_abort)
834{
836
837 if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
838 (fr_state_to_request(inst->auth.state_tree, request) < 0)) {
839 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
840 }
841
842 return CALL_RECV(generic);
843}
844
845RESUME(auth_cont_abort)
846{
847 fr_process_state_t const *state;
848
849 if (!request->reply->code) request->reply->code = FR_TACACS_CODE_AUTH_RESTART;
850
851 UPDATE_STATE(reply);
852
853 fr_assert(state->send != NULL);
854 return CALL_SEND_STATE(state);
855}
856
857
864
865
866RESUME(autz_request)
867{
868 rlm_rcode_t rcode = RESULT_RCODE;
869 fr_process_state_t const *state;
870
872
874
875 /*
876 * See if the return code from "recv" which says we reject, or continue.
877 */
878 UPDATE_STATE(packet);
879
880 /*
881 * Nothing set the reply, so let's see if we need to do so.
882 *
883 * If the admin didn't set authorization-status, just
884 * use the defaults from the state machine.
885 */
886 if (!request->reply->code) {
887 request->reply->code = reply_code(request, attr_tacacs_authorization_status,
888 author_status_to_packet_code, state, NULL, rcode);
889 if (!request->reply->code) request->reply->code = FR_TACACS_CODE_AUTZ_ERROR;
890
891 } else {
892 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
893 }
894
895 RDEBUG("Reply packet type set to %s", fr_tacacs_packet_names[request->reply->code]);
896
897 UPDATE_STATE(reply);
898
899 fr_assert(state->send != NULL);
900 return CALL_SEND_STATE(state);
901}
902
907
908RESUME(acct_type)
909{
910 static const fr_process_rcode_t acct_type_rcode = {
919 };
920
921 rlm_rcode_t rcode = RESULT_RCODE;
922 fr_process_state_t const *state;
923
925
926 /*
927 * One more chance to override
928 */
929 if (!request->reply->code) {
931 NULL, acct_type_rcode, rcode);
932 if (!request->reply->code) request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
933 } else {
934 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
935 }
936
937 UPDATE_STATE(reply);
938
939 fr_assert(state->send != NULL);
940 return state->send(p_result, mctx, request);
941}
942
943static const bool acct_flag_valid[8] = {
944 false, true, true, false, /* invalid, start, stop, invalid */
945 true, true, false, false, /* watchdog - no update, watchdog - update, invalid, invalid */
946};
947
948RECV(accounting_request)
949{
950 fr_pair_t *vp;
951
952 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
953
954 /*
955 * RFC 8907 Section 7.2
956 */
957 if (vp && !acct_flag_valid[(vp->vp_uint8 & 0x0e) >> 1]) {
958 RWDEBUG("Invalid accounting request flag field %02x", vp->vp_uint8);
959 return CALL_SEND_TYPE(FR_TACACS_CODE_ACCT_ERROR);
960 }
961
962 return CALL_RECV(generic);
963}
964
965RESUME(accounting_request)
966{
967 rlm_rcode_t rcode = RESULT_RCODE;
968 fr_pair_t *vp;
969 CONF_SECTION *cs;
970 fr_dict_enum_value_t const *dv;
971 fr_process_state_t const *state;
973
975
977
978 UPDATE_STATE(packet);
979
980 /*
981 * Nothing set the reply, so let's see if we need to do so.
982 *
983 * If the admin didn't set accounting-status, just
984 * use the defaults from the state machine.
985 */
986 if (!request->reply->code) {
987 request->reply->code = reply_code(request, attr_tacacs_accounting_status,
988 acct_status_to_packet_code, state, NULL, rcode);
989 } else {
990 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
991 }
992
993 /*
994 * Something set the reply code, so we reply and don't run "accounting foo { ... }"
995 */
996 if (request->reply->code) {
997 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->packet->code));
998
999 RDEBUG("Reply packet type was set to %s", fr_tacacs_packet_names[request->reply->code]);
1000
1001 UPDATE_STATE(reply);
1002
1003 fr_assert(state->send != NULL);
1004 return CALL_SEND_STATE(state);
1005 }
1006
1007 /*
1008 * Run accounting foo { ... }
1009 */
1010 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
1011 if (!vp) {
1012 fail:
1013 request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
1014 UPDATE_STATE(reply);
1015 fr_assert(state->send != NULL);
1016 return CALL_SEND_STATE(state);
1017 }
1018
1019 dv = fr_dict_enum_by_value(vp->da, &vp->data);
1020 if (!dv) goto fail;
1021
1022 cs = cf_section_find(inst->server_cs, "accounting", dv->name);
1023 if (!cs) {
1024 RDEBUG2("No 'accounting %s { ... }' section found - skipping...", dv->name);
1025 goto fail;
1026 }
1027
1028 /*
1029 * Run the "accounting foo { ... }" section.
1030 *
1031 * And continue with sending the generic reply.
1032 */
1033 return unlang_module_yield_to_section(RESULT_P, request,
1034 cs, RLM_MODULE_NOOP, resume_acct_type,
1035 NULL, 0, mctx->rctx);
1036}
1037
1038static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1039{
1040 fr_process_state_t const *state;
1041
1043
1045 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->packet->code));
1046
1047 request->component = "tacacs";
1048 request->module = NULL;
1049 fr_assert(request->proto_dict == dict_tacacs);
1050
1051 UPDATE_STATE(packet);
1052
1053 if (!state->recv) {
1054 REDEBUG("Invalid packet type (%u)", request->packet->code);
1056 }
1057
1058 // @todo - debug stuff!
1059// tacacs_packet_debug(request, request->packet, &request->request_pairs, true);
1060
1061 if (unlikely(request_is_dynamic_client(request))) {
1062 return new_client(p_result, mctx, request);
1063 }
1064
1065 return state->recv(p_result, mctx, request);
1066}
1067
1068static int mod_instantiate(module_inst_ctx_t const *mctx)
1069{
1070 process_tacacs_t *inst = talloc_get_type_abort(mctx->mi->data, process_tacacs_t);
1071
1072 inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf));
1073
1074 FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, >=, 1);
1075 FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, <=, 8);
1076
1077 FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, >=, 64);
1078 FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, <=, (1 << 18));
1079
1080 inst->auth.state_tree = fr_state_tree_init(inst, attr_tacacs_state, main_config->spawn_workers, inst->auth.max_session,
1081 inst->auth.session_timeout, inst->auth.state_server_id,
1082 fr_hash_string(cf_section_name2(inst->server_cs)));
1083 return 0;
1084}
1085
1086static int mod_bootstrap(module_inst_ctx_t const *mctx)
1087{
1088 CONF_SECTION *server_cs = cf_item_to_section(cf_parent(mctx->mi->conf));
1089
1090 if (virtual_server_section_attribute_define(server_cs, "authenticate", attr_auth_type) < 0) return -1;
1091
1092 return 0;
1093}
1094
1095/*
1096 * rcodes not listed under a packet_type
1097 * mean that the packet code will not be
1098 * changed.
1099 */
1100static fr_process_state_t const process_state[] = {
1101 /*
1102 * Authentication
1103 */
1105 .packet_type = {
1112 },
1113 .default_rcode = RLM_MODULE_NOOP,
1114 .recv = recv_auth_start,
1115 .resume = resume_auth_start,
1116 .section_offset = offsetof(process_tacacs_sections_t, auth_start),
1117 },
1119 .packet_type = {
1125 },
1126 .default_rcode = RLM_MODULE_NOOP,
1127 .result_rcode = RLM_MODULE_OK,
1128 .send = send_generic,
1129 .resume = resume_auth_pass,
1130 .section_offset = offsetof(process_tacacs_sections_t, auth_pass),
1131 },
1133 .packet_type = {
1139 },
1140 .default_rcode = RLM_MODULE_NOOP,
1141 .result_rcode = RLM_MODULE_REJECT,
1142 .send = send_generic,
1143 .resume = resume_auth_fail,
1144 .section_offset = offsetof(process_tacacs_sections_t, auth_fail),
1145 },
1147 .packet_type = {
1153 },
1154 .default_rcode = RLM_MODULE_NOOP,
1155 .result_rcode = RLM_MODULE_OK,
1156 .send = send_generic,
1157 .resume = resume_auth_get,
1158 .section_offset = offsetof(process_tacacs_sections_t, auth_getdata),
1159 },
1161 .packet_type = {
1167 },
1168 .default_rcode = RLM_MODULE_NOOP,
1169 .result_rcode = RLM_MODULE_OK,
1170 .send = send_generic,
1171 .resume = resume_auth_get,
1172 .section_offset = offsetof(process_tacacs_sections_t, auth_getpass),
1173 },
1175 .packet_type = {
1181 },
1182 .default_rcode = RLM_MODULE_NOOP,
1183 .result_rcode = RLM_MODULE_OK,
1184 .send = send_generic,
1185 .resume = resume_auth_get,
1186 .section_offset = offsetof(process_tacacs_sections_t, auth_getuser),
1187 },
1189 .packet_type = {
1190 },
1191 .default_rcode = RLM_MODULE_NOOP,
1192 .result_rcode = RLM_MODULE_OK,
1193 .send = send_generic,
1194 .resume = resume_auth_restart,
1195 .section_offset = offsetof(process_tacacs_sections_t, auth_restart),
1196 },
1198 .packet_type = {
1199 },
1200 .default_rcode = RLM_MODULE_NOOP,
1201 .result_rcode = RLM_MODULE_REJECT,
1202 .send = send_generic,
1203 .resume = resume_auth_restart,
1204 .section_offset = offsetof(process_tacacs_sections_t, auth_error),
1205 },
1206
1208 .packet_type = {
1215 },
1216 .default_rcode = RLM_MODULE_NOOP,
1217 .result_rcode = RLM_MODULE_OK,
1218 .recv = recv_auth_cont,
1219 .resume = resume_auth_start, /* we go back to running 'authenticate', etc. */
1220 .section_offset = offsetof(process_tacacs_sections_t, auth_cont),
1221 },
1223 .packet_type = {
1230 },
1231 .default_rcode = RLM_MODULE_NOOP,
1232 .result_rcode = RLM_MODULE_REJECT,
1233 .recv = recv_auth_cont_abort,
1234 .resume = resume_auth_cont_abort,
1235 .section_offset = offsetof(process_tacacs_sections_t, auth_cont_abort),
1236 },
1237
1238 /*
1239 * Authorization
1240 */
1242 .packet_type = {
1247
1254 },
1255 .default_rcode = RLM_MODULE_NOOP,
1256 .recv = recv_generic,
1257 .resume = resume_autz_request,
1258 .section_offset = offsetof(process_tacacs_sections_t, autz_request),
1259 },
1261 .packet_type = {
1268 },
1269 .default_rcode = RLM_MODULE_NOOP,
1270 .result_rcode = RLM_MODULE_OK,
1271 .send = send_generic,
1272 .resume = resume_send_generic,
1273 .section_offset = offsetof(process_tacacs_sections_t, autz_pass_add),
1274 },
1276 .packet_type = {
1283 },
1284 .default_rcode = RLM_MODULE_NOOP,
1285 .result_rcode = RLM_MODULE_OK,
1286 .send = send_generic,
1287 .resume = resume_send_generic,
1288 .section_offset = offsetof(process_tacacs_sections_t, autz_pass_replace),
1289 },
1291 .packet_type = {
1292 },
1293 .default_rcode = RLM_MODULE_NOOP,
1294 .result_rcode = RLM_MODULE_REJECT,
1295 .send = send_generic,
1296 .resume = resume_send_generic,
1297 .section_offset = offsetof(process_tacacs_sections_t, autz_fail),
1298 },
1300 .packet_type = {
1301 },
1302 .default_rcode = RLM_MODULE_NOOP,
1303 .result_rcode = RLM_MODULE_REJECT,
1304 .send = send_generic,
1305 .resume = resume_send_generic,
1306 .section_offset = offsetof(process_tacacs_sections_t, autz_error),
1307 },
1308
1309 /*
1310 * Accounting
1311 */
1313 .packet_type = {
1320 },
1321 .default_rcode = RLM_MODULE_NOOP,
1322 .recv = recv_accounting_request,
1323 .resume = resume_accounting_request,
1324 .section_offset = offsetof(process_tacacs_sections_t, acct_request),
1325 },
1327 .packet_type = {
1334 },
1335 .default_rcode = RLM_MODULE_NOOP,
1336 .result_rcode = RLM_MODULE_OK,
1337 .send = send_generic,
1338 .resume = resume_send_generic,
1339 .section_offset = offsetof(process_tacacs_sections_t, acct_success),
1340 },
1342 .packet_type = {
1343 },
1344 .default_rcode = RLM_MODULE_NOOP,
1345 .result_rcode = RLM_MODULE_FAIL,
1346 .send = send_generic,
1347 .resume = resume_send_generic,
1348 .section_offset = offsetof(process_tacacs_sections_t, acct_error),
1349 },
1351 .packet_type = {
1356
1363 },
1364 .default_rcode = RLM_MODULE_NOOP,
1365 .result_rcode = RLM_MODULE_HANDLED,
1366 .send = send_generic,
1367 .resume = resume_send_generic,
1368 .section_offset = offsetof(process_tacacs_sections_t, do_not_respond),
1369 }
1370};
1371
1372
1374 /**
1375 * Basically, the TACACS+ protocol use same type "authenticate" to handle
1376 * Start and Continue requests. (yep, you're right. it's horrible)
1377 * Therefore, we split the same "auth" type into two different sections just
1378 * to allow the user to have different logic for that.
1379 *
1380 * If you want to cry, just take a look at
1381 *
1382 * https://tools.ietf.org/html/rfc8907 Section 4.
1383 *
1384 * This should be an abject lesson in how NOT to design a
1385 * protocol. Pretty much everything they did was wrong.
1386 */
1387 {
1388 .section = SECTION_NAME("recv", "Authentication-Start"),
1389 .actions = &mod_actions_authenticate,
1390 .offset = PROCESS_CONF_OFFSET(auth_start),
1391 },
1392 {
1393 .section = SECTION_NAME("send", "Authentication-Pass"),
1395 .offset = PROCESS_CONF_OFFSET(auth_pass),
1396 },
1397 {
1398 .section = SECTION_NAME("send", "Authentication-Fail"),
1400 .offset = PROCESS_CONF_OFFSET(auth_fail),
1401 },
1402 {
1403 .section = SECTION_NAME("send", "Authentication-GetData"),
1405 .offset = PROCESS_CONF_OFFSET(auth_getdata),
1406 },
1407 {
1408 .section = SECTION_NAME("send", "Authentication-GetUser"),
1410 .offset = PROCESS_CONF_OFFSET(auth_getuser),
1411 },
1412 {
1413 .section = SECTION_NAME("send", "Authentication-GetPass"),
1415 .offset = PROCESS_CONF_OFFSET(auth_getpass),
1416 },
1417 {
1418 .section = SECTION_NAME("send", "Authentication-Restart"),
1420 .offset = PROCESS_CONF_OFFSET(auth_restart),
1421 },
1422 {
1423 .section = SECTION_NAME("send", "Authentication-Error"),
1425 .offset = PROCESS_CONF_OFFSET(auth_error),
1426 },
1427 {
1428 .section = SECTION_NAME("recv", "Authentication-Continue"),
1430 .offset = PROCESS_CONF_OFFSET(auth_cont),
1431 },
1432 {
1433 .section = SECTION_NAME("recv", "Authentication-Continue-Abort"),
1435 .offset = PROCESS_CONF_OFFSET(auth_cont_abort),
1436 },
1437
1438 {
1439 .section = SECTION_NAME("authenticate", CF_IDENT_ANY),
1441 },
1442
1443 /* authorization */
1444
1445 {
1446 .section = SECTION_NAME("recv", "Authorization-Request"),
1448 .offset = PROCESS_CONF_OFFSET(autz_request),
1449 },
1450 {
1451 .section = SECTION_NAME("send", "Authorization-Pass-Add"),
1453 .offset = PROCESS_CONF_OFFSET(autz_pass_add),
1454 },
1455 {
1456 .section = SECTION_NAME("send", "Authorization-Pass-Replace"),
1458 .offset = PROCESS_CONF_OFFSET(autz_pass_replace),
1459 },
1460 {
1461 .section = SECTION_NAME("send", "Authorization-Fail"),
1463 .offset = PROCESS_CONF_OFFSET(autz_fail),
1464 },
1465 {
1466 .section = SECTION_NAME("send", "Authorization-Error"),
1468 .offset = PROCESS_CONF_OFFSET(autz_error),
1469 },
1470
1471 /* accounting */
1472
1473 {
1474 .section = SECTION_NAME("recv", "Accounting-Request"),
1476 .offset = PROCESS_CONF_OFFSET(acct_request),
1477 },
1478 {
1479 .section = SECTION_NAME("send", "Accounting-Success"),
1481 .offset = PROCESS_CONF_OFFSET(acct_success),
1482 },
1483 {
1484 .section = SECTION_NAME("send", "Accounting-Error"),
1486 .offset = PROCESS_CONF_OFFSET(acct_error),
1487 },
1488
1489 {
1490 .section = SECTION_NAME("accounting", CF_IDENT_ANY),
1492 },
1493
1494 {
1495 .section = SECTION_NAME("send", "Do-Not-Respond"),
1497 .offset = PROCESS_CONF_OFFSET(do_not_respond),
1498 },
1499
1500 DYNAMIC_CLIENT_SECTIONS,
1501
1503};
1504
1505
1508 .common = {
1509 .magic = MODULE_MAGIC_INIT,
1510 .name = "tacacs",
1511 .config = config,
1513 MODULE_RCTX(process_rctx_t),
1514 .bootstrap = mod_bootstrap,
1515 .instantiate = mod_instantiate
1516 },
1517 .process = mod_process,
1518 .compile_list = compile_list,
1519 .dict = &dict_tacacs,
1520 .packet_type = &attr_packet_type
1521};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
static int const char char buffer[256]
Definition acutest.h:576
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:662
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:522
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition cf_parse.h:339
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:428
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:599
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_filename(_cf)
Definition cf_util.h:107
#define CF_IDENT_ANY
Definition cf_util.h:78
#define MEM(x)
Definition debug.h:36
fr_value_box_t const ** out
Enumeration value.
Definition dict.h:263
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:274
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:287
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:237
fr_dict_enum_value_t const * fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition dict_util.c:3465
char const * name
Enum name.
Definition dict.h:234
Specifies an attribute which must be present for the module to function.
Definition dict.h:273
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:286
Specifies a value which must be present for the module to function.
Definition dict.h:262
Value of an enumerated attribute.
Definition dict.h:233
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
uint32_t fr_hash(void const *data, size_t size)
Definition hash.c:812
uint32_t fr_hash_string(char const *p)
Definition hash.c:865
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:93
fr_dict_attr_t const * attr_user_name
Definition base.c:104
static fr_dict_attr_t const * attr_module_failure_message
Definition log.c:206
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
int fr_debug_lvl
Definition log.c:40
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:71
main_config_t const * main_config
Main server configuration.
Definition main_config.c:58
bool spawn_workers
Should the server spawn threads.
Definition main_config.h:58
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
unsigned char uint8_t
#define UINT8_MAX
unlang_mod_actions_t const mod_actions_authenticate
Definition mod_action.c:30
unlang_mod_actions_t const mod_actions_accounting
Definition mod_action.c:78
unlang_mod_actions_t const mod_actions_authorize
Definition mod_action.c:46
unlang_mod_actions_t const mod_actions_postauth
Definition mod_action.c:93
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:64
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
static void fr_nbo_from_uint32(uint8_t out[static sizeof(uint32_t)], uint32_t num)
Write out an unsigned 32bit integer in wire format (big endian)
Definition nbo.h:61
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2319
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2938
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:775
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:698
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1343
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:287
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:188
static const virtual_server_compile_t compile_list[]
Definition base.c:214
static fr_process_state_t const process_state[]
Definition base.c:69
RESUME_FLAG(recv_bfd, UNUSED,)
Definition base.c:119
static fr_dict_attr_t const * attr_module_success_message
Definition base.c:37
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition base.c:749
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition base.c:401
static fr_dict_attr_t const * attr_user_password
Definition base.c:66
static fr_dict_attr_t const * attr_stripped_user_name
Definition base.c:59
static const conf_parser_t session_config[]
Definition base.c:172
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition base.c:887
static fr_dict_attr_t const * attr_auth_type
Definition base.c:56
static fr_value_box_t const * enum_auth_type_reject
Definition base.c:94
static const conf_parser_t auth_config[]
Definition base.c:180
static fr_value_box_t const * enum_auth_type_accept
Definition base.c:93
static const conf_parser_t config[]
Definition base.c:186
fr_process_module_t process_tacacs
Definition base.c:1507
static fr_dict_attr_t const * attr_tacacs_authentication_flags
Definition base.c:56
CONF_SECTION * autz_pass_add
Definition base.c:147
fr_pair_list_t list
copied from the request
Definition base.c:193
CONF_SECTION * new_client
Definition base.c:158
static fr_value_box_t const * enum_tacacs_auth_type_ascii
Definition base.c:119
CONF_SECTION * add_client
Definition base.c:159
CONF_SECTION * auth_pass
Definition base.c:135
CONF_SECTION * auth_start
Definition base.c:134
CONF_SECTION * acct_success
Definition base.c:153
CONF_SECTION * auth_error
Definition base.c:141
CONF_SECTION * acct_request
Definition base.c:152
CONF_SECTION * auth_cont_abort
Definition base.c:144
fr_dict_attr_autoload_t process_tacacs_dict_attr[]
Definition base.c:80
static fr_dict_attr_t const * attr_tacacs_user_message
Definition base.c:73
CONF_SECTION * server_cs
Our virtual server.
Definition base.c:177
static fr_dict_attr_t const * attr_chap_password
Definition base.c:77
CONF_SECTION * auth_cont
Definition base.c:143
static const uint32_t acct_status_to_packet_code[UINT8_MAX+1]
Definition base.c:903
CONF_SECTION * auth_restart
Definition base.c:140
static const uint32_t authen_status_to_packet_code[UINT8_MAX+1]
Definition base.c:373
process_tacacs_sections_t sections
Pointers to various config sections we need to execute.
Definition base.c:181
static uint32_t reply_code(request_t *request, fr_dict_attr_t const *status_da, uint32_t const status2code[static UINT8_MAX+1], fr_process_state_t const *state, fr_process_rcode_t const process_rcode, rlm_rcode_t rcode)
Try and determine what the response packet type should be.
Definition base.c:291
static fr_dict_t const * dict_tacacs
Definition base.c:39
fr_time_delta_t session_timeout
Maximum time between the last response and next request.
Definition base.c:164
uint32_t rounds
how many rounds were taken
Definition base.c:190
fr_dict_enum_autoload_t process_tacacs_dict_enum[]
Definition base.c:122
#define COPY_MISSING(_attr)
static fr_dict_attr_t const * attr_tacacs_session_id
Definition base.c:70
static fr_dict_attr_t const * attr_tacacs_server_message
Definition base.c:69
CONF_SECTION * autz_error
Definition base.c:150
CONF_SECTION * do_not_respond
Definition base.c:156
static const uint32_t author_status_to_packet_code[UINT8_MAX+1]
Definition base.c:858
static fr_value_box_t const * enum_auth_flags_noecho
Definition base.c:118
uint8_t state_server_id
Sets a specific byte in the state to allow the authenticating server to be identified in packet captu...
Definition base.c:169
CONF_SECTION * autz_pass_replace
Definition base.c:148
uint32_t reply
for multiround state machine
Definition base.c:191
#define EXTRACT(_attr)
CONF_SECTION * auth_getuser
Definition base.c:138
static fr_dict_attr_t const * attr_tacacs_privilege_level
Definition base.c:67
CONF_SECTION * auth_getdata
Definition base.c:137
static fr_dict_attr_t const * attr_tacacs_authentication_type
Definition base.c:57
CONF_SECTION * acct_error
Definition base.c:154
static fr_dict_attr_t const * attr_tacacs_accounting_status
Definition base.c:62
CONF_SECTION * autz_request
Definition base.c:146
uint32_t max_session
Maximum ongoing session allowed.
Definition base.c:165
static const bool acct_flag_valid[8]
Definition base.c:943
static fr_dict_attr_t const * attr_tacacs_authentication_service
Definition base.c:58
static fr_dict_attr_t const * attr_tacacs_authentication_status
Definition base.c:59
CONF_SECTION * deny_client
Definition base.c:160
CONF_SECTION * auth_fail
Definition base.c:136
static fr_dict_attr_t const * attr_tacacs_authentication_action
Definition base.c:55
static fr_dict_attr_t const * attr_tacacs_client_port
Definition base.c:65
process_tacacs_auth_t auth
Authentication configuration.
Definition base.c:184
static fr_dict_attr_t const * attr_tacacs_remote_address
Definition base.c:68
uint8_t seq_no
sequence number of last request.
Definition base.c:192
fr_state_tree_t * state_tree
State tree to link multiple requests/responses.
Definition base.c:173
#define COPY(_attr)
static fr_dict_attr_t const * attr_tacacs_action
Definition base.c:54
static fr_dict_attr_t const * attr_tacacs_sequence_number
Definition base.c:71
static fr_dict_attr_t const * attr_tacacs_authorization_status
Definition base.c:61
CONF_SECTION * autz_fail
Definition base.c:149
static fr_dict_attr_t const * attr_tacacs_accounting_flags
Definition base.c:63
static fr_dict_attr_t const * attr_tacacs_data
Definition base.c:66
static fr_dict_attr_t const * attr_tacacs_state
Definition base.c:72
uint32_t max_rounds
maximum number of authentication rounds allowed
Definition base.c:167
uint32_t session_id
current session ID
Definition base.c:179
CONF_SECTION * auth_getpass
Definition base.c:139
fr_dict_autoload_t process_tacacs_dict[]
Definition base.c:42
static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
Definition base.c:232
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition process.h:55
#define PROCESS_CONF_OFFSET(_x)
Definition process.h:79
module_t common
Common fields for all loadable modules.
Common public symbol definition for all process modules.
char const * fr_tacacs_packet_names[FR_TACACS_CODE_MAX]
Definition base.c:120
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG(fmt,...)
Definition radclient.h:53
static void send_reply(int sockfd, fr_channel_data_t *reply)
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:47
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:48
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:43
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:52
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:49
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:51
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:50
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:53
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:46
#define request_is_dynamic_client(_x)
Definition request.h:188
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
#define request_data_talloc_add(_request, _unique_ptr, _unique_int, _type, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
static unlang_action_t process_rcode(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
void * data
Module's instance data.
Definition module.h:291
#define MODULE_RCTX(_ctype)
Definition module.h:257
#define MODULE_INST(_ctype)
Definition module.h:255
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
#define pair_delete_reply(_pair_or_da)
Delete a fr_pair_t in the reply list.
Definition pair.h:181
#define pair_append_reply(_attr, _da)
Allocate and append a fr_pair_t to reply list.
Definition pair.h:47
fr_state_tree_t * fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, bool thread_safe, uint32_t max_sessions, fr_time_delta_t timeout, uint8_t server_id, uint32_t context_id)
Initialise a new state tree.
Definition state.c:220
void fr_state_discard(fr_state_tree_t *state, request_t *request)
Called when sending an Access-Accept/Access-Reject to discard state information.
Definition state.c:605
int fr_request_to_state(fr_state_tree_t *state, request_t *request)
Transfer ownership of the state fr_pair_ts and ctx, back to a state entry.
Definition state.c:736
int fr_state_to_request(fr_state_tree_t *state, request_t *request)
Copy a pointer to the head of the list of state fr_pair_ts (and their ctx) into the request.
Definition state.c:659
unlang_action_t unlang_module_yield_to_section(unlang_result_t *p_result, request_t *request, CONF_SECTION *subcs, rlm_rcode_t default_rcode, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Definition module.c:236
eap_aka_sim_process_conf_t * inst
#define RESUME(_x)
fr_pair_t * vp
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
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
@ 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 FR_TACACS_PACKET_CODE_VALID(_code)
Definition tacacs.h:322
#define packet_is_authen_start_request(p)
3.4.
Definition tacacs.h:49
@ FR_TACACS_CODE_ACCT_ERROR
Definition tacacs.h:315
@ FR_TACACS_CODE_DO_NOT_RESPOND
Definition tacacs.h:318
@ FR_TACACS_CODE_ACCT_REQUEST
Definition tacacs.h:313
@ FR_TACACS_CODE_AUTZ_REQUEST
Definition tacacs.h:307
@ FR_TACACS_CODE_AUTH_GETDATA
Definition tacacs.h:298
@ FR_TACACS_CODE_AUTH_RESTART
Definition tacacs.h:301
@ FR_TACACS_CODE_AUTZ_PASS_REPLACE
Definition tacacs.h:309
@ FR_TACACS_CODE_AUTH_GETUSER
Definition tacacs.h:299
@ FR_TACACS_CODE_AUTH_GETPASS
Definition tacacs.h:300
@ FR_TACACS_CODE_AUTZ_FAIL
Definition tacacs.h:310
@ FR_TACACS_CODE_AUTH_CONT_ABORT
Definition tacacs.h:305
@ FR_TACACS_CODE_AUTH_PASS
Definition tacacs.h:296
@ FR_TACACS_CODE_AUTH_CONT
Definition tacacs.h:304
@ FR_TACACS_CODE_AUTZ_PASS_ADD
Definition tacacs.h:308
@ FR_TACACS_CODE_AUTH_START
Definition tacacs.h:295
@ FR_TACACS_CODE_AUTH_FAIL
Definition tacacs.h:297
@ FR_TACACS_CODE_AUTH_ERROR
Definition tacacs.h:302
@ FR_TACACS_CODE_AUTZ_ERROR
Definition tacacs.h:311
@ FR_TACACS_CODE_ACCT_SUCCESS
Definition tacacs.h:314
@ 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 talloc_get_type_abort_const
Definition talloc.h:287
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
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
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:742
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4156
static size_t char ** out
Definition value.h:1023
int virtual_server_section_attribute_define(CONF_SECTION *server_cs, char const *subcs_name, fr_dict_attr_t const *da)
Define a values for Auth-Type attributes by the sections present in a virtual-server.
section_name_t const * section
Identifier for the section.
#define COMPILE_TERMINATOR
Processing sections which are allowed in this virtual server.