The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 9915a48c351ba3282b800186351ee2317526d2b4 $
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/tacacs/tacacs.h>
29#include <freeradius-devel/unlang/call.h>
30#include <freeradius-devel/unlang/xlat_func.h>
31#include <freeradius-devel/util/debug.h>
32
33#include <freeradius-devel/protocol/tacacs/tacacs.h>
34
36static fr_dict_t const *dict_tacacs;
37
40 { .out = &dict_freeradius, .proto = "freeradius" },
41 { .out = &dict_tacacs, .proto = "tacacs" },
43};
44
50
57
61
71
75
78 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
79 { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
80 { .out = &attr_module_success_message, .name = "Module-Success-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
81 { .out = &attr_stripped_user_name, .name = "Stripped-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
82 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
83
84 { .out = &attr_tacacs_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
85 { .out = &attr_tacacs_authentication_flags, .name = "Authentication-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
86 { .out = &attr_tacacs_authentication_type, .name = "Authentication-Type", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
87 { .out = &attr_tacacs_authentication_service, .name = "Authentication-Service", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
88
89 { .out = &attr_tacacs_authentication_status, .name = "Authentication-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
90 { .out = &attr_tacacs_authorization_status, .name = "Authorization-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
91
92 { .out = &attr_tacacs_accounting_status, .name = "Accounting-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
93 { .out = &attr_tacacs_accounting_flags, .name = "Accounting-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
94
95 { .out = &attr_tacacs_client_port, .name = "Client-Port", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
96 { .out = &attr_tacacs_data, .name = "Data", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
97 { .out = &attr_tacacs_privilege_level, .name = "Privilege-Level", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
98 { .out = &attr_tacacs_remote_address, .name = "Remote-Address", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
99 { .out = &attr_tacacs_authentication_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
100 { .out = &attr_tacacs_session_id, .name = "Packet.Session-Id", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
101 { .out = &attr_tacacs_sequence_number, .name = "Packet.Sequence-Number", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
102 { .out = &attr_tacacs_server_message, .name = "Server-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
103 { .out = &attr_tacacs_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
104 { .out = &attr_tacacs_user_message, .name = "User-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
105
106 { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
107 { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
108 { .out = &attr_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
109
111};
112
117
120 { .out = &enum_auth_type_accept, .name = "Accept", .attr = &attr_auth_type },
121 { .out = &enum_auth_type_reject, .name = "Reject", .attr = &attr_auth_type },
122 { .out = &enum_auth_flags_noecho, .name = "No-Echo", .attr = &attr_tacacs_authentication_flags },
123 { .out = &enum_tacacs_auth_type_ascii, .name = "ASCII", .attr = &attr_tacacs_authentication_type },
125};
126
127
159
160typedef struct {
161 fr_state_config_t session; //!< track state session information.
162 fr_state_tree_t *state_tree; //!< State tree to link multiple requests/responses.
164
165typedef struct {
166 CONF_SECTION *server_cs; //!< Our virtual server.
167
168 uint32_t session_id; //!< current session ID
169
170 process_tacacs_sections_t sections; //!< Pointers to various config sections
171 ///< we need to execute
172
173 process_tacacs_auth_t auth; //!< Authentication configuration.
174
175
177
178typedef struct {
179 uint32_t reply; //!< for multiround state machine
180 uint8_t seq_no; //!< sequence number of last request.
181 fr_pair_list_t list; //!< copied from the request
183
184
185#define PROCESS_PACKET_TYPE fr_tacacs_packet_code_t
186#define PROCESS_CODE_MAX FR_TACACS_CODE_MAX
187#define PROCESS_CODE_DO_NOT_RESPOND FR_TACACS_CODE_DO_NOT_RESPOND
188#define PROCESS_PACKET_CODE_VALID FR_TACACS_PACKET_CODE_VALID
189#define PROCESS_INST process_tacacs_t
190#define PROCESS_CODE_DYNAMIC_CLIENT FR_TACACS_CODE_AUTH_PASS
191
192#include <freeradius-devel/server/process.h>
193
194static const conf_parser_t auth_config[] = {
195 { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) state_session_config },
196
198};
199
200static const conf_parser_t config[] = {
201 { FR_CONF_POINTER("Authentication", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) auth_config,
202 .offset = offsetof(process_tacacs_t, auth), },
203
205};
206
207
208/*
209 * Synthesize a State attribute from connection && session information.
210 */
211static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
212{
213 uint64_t hash;
214 uint32_t sequence;
215 fr_pair_t *vp;
216
217 if (!request->async->listen) return -1;
218
219 vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_session_id);
220 if (!vp) return -1;
221
222 hash = fr_hash64(&vp->vp_uint32, sizeof(vp->vp_uint32));
223
224 vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_sequence_number);
225 if (!vp) return -1;
226
227 /*
228 * Requests have odd sequence numbers, and replies have even sequence numbers.
229 * So if we want to synthesize a state in a reply which gets matched with the next
230 * request, we have to add 2 to it.
231 */
232 sequence = vp->vp_uint8 + ((int) reply << 1);
233 hash = fr_hash64_update(&sequence, sizeof(sequence), hash);
234
235 hash = fr_hash64_update(&request->async->listen, sizeof(request->async->listen), hash);
236
238 if (!vp) return -1;
239
240 (void) fr_pair_value_memdup(vp, (uint8_t const *) &hash, sizeof(hash), false);
241
243
244 return 0;
245}
246
247/** Try and determine what the response packet type should be
248 *
249 * We check three sources:
250 * - reply.``<status_attr>``
251 * - reply.Packet-Type
252 * - State machine packet type assignments for the section rcode
253 *
254 * @param[in] request The current request.
255 * @param[in] status_da Specialised status attribute.
256 * @param[in] status2code Mapping table of *packet* status types to rcodes.
257 * @param[in] state Mappings for process state machine
258 * @param[in] process_rcode Mappings for Auth-Type / Acct-Type, which don't use the process state machine
259 * @param[in] rcode The last section rcode.
260 * @return
261 * - >0 if we determined a reply code.
262 * - 0 if we couldn't - Usually indicates additional sections should be run.
263 */
264static uint32_t reply_code(request_t *request, fr_dict_attr_t const *status_da,
265 uint32_t const status2code[static UINT8_MAX + 1],
266 fr_process_state_t const *state, fr_process_rcode_t const process_rcode, rlm_rcode_t rcode)
267{
268 fr_pair_t *vp;
269 uint32_t code;
270
271 /*
272 * First check the protocol attribute for this packet type.
273 *
274 * Should be one of:
275 * - Authentication-Status
276 * - Authorization-Status
277 * - Accounting-Status
278 */
279 fr_assert(status_da->type == FR_TYPE_UINT8);
280
281 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, status_da);
282 if (vp) {
283 code = status2code[vp->vp_uint8];
284 if (FR_TACACS_PACKET_CODE_VALID(code)) {
285 RDEBUG("Setting reply Packet-Type from %pP", vp);
286 return code;
287 }
288
289 REDEBUG("Ignoring invalid status %pP", vp);
290 }
291
292 if (state) {
293 code = state->packet_type[rcode];
294 if (FR_TACACS_PACKET_CODE_VALID(code) || (code == FR_TACACS_CODE_DO_NOT_RESPOND)) return code;
295 }
296
297 if (process_rcode) {
298 code = process_rcode[rcode];
299 if (FR_TACACS_PACKET_CODE_VALID(code) || (code == FR_TACACS_CODE_DO_NOT_RESPOND)) return code;
300 }
301
302 /*
303 * Otherwise use Packet-Type (if set)
304 */
305 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
306 if (vp && (FR_TACACS_PACKET_CODE_VALID(vp->vp_uint32) || (vp->vp_uint32 == FR_TACACS_CODE_DO_NOT_RESPOND))) {
307 RDEBUG("Setting reply Packet-Type from %pV", &vp->data);
308 return vp->vp_uint32;
309 }
310
311 return 0;
312}
313
314RECV(auth_start)
315{
316 fr_process_state_t const *state;
317 fr_pair_t *vp;
318
319 /*
320 * Only "Login" is supported. The others are "change password" and "sendauth", which aren't
321 * used.
322 */
323 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_action);
324 if (!vp) {
325 fail:
326 request->reply->code = FR_TACACS_CODE_AUTH_ERROR;
327 UPDATE_STATE(reply);
328
329 fr_assert(state->send != NULL);
330 return CALL_SEND_STATE(state);
331 }
332
333 if (vp->vp_uint8 != FR_ACTION_VALUE_LOGIN) {
334 RDEBUG("Invalid authentication action %u", vp->vp_uint8);
335 goto fail;
336 }
337
338 /*
339 * There is no state to restore, so we just run the section as normal.
340 */
341
342 return CALL_RECV(generic);
343}
344
345RESUME(auth_type);
346
356
357RESUME(auth_start)
358{
359 rlm_rcode_t rcode = RESULT_RCODE;
360 fr_pair_t *vp;
361 CONF_SECTION *cs;
362 fr_dict_enum_value_t const *dv;
363 fr_process_state_t const *state;
365
367
369
370 /*
371 * See if the return code from "recv" which says we reject, or continue.
372 */
373 UPDATE_STATE(packet);
374
375 /*
376 * Nothing set the reply, so let's see if we need to do so.
377 *
378 * If the admin didn't set authentication-status, just
379 * use the defaults from the state machine.
380 */
381 if (!request->reply->code) {
382 request->reply->code = reply_code(request,
384 authen_status_to_packet_code, state, NULL, rcode);
385 } else {
386 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
387 }
388
389 /*
390 * Check for multi-round authentication.
391 *
392 * We only run the automatic state machine (start -> getuser -> getpass -> pass/fail)
393 * when the admin does NOT set any reply type, or any reply authentication status.
394 *
395 * However, do DO always save and restore the attributes from the start packet, so that they are
396 * visible in a later packet.
397 */
398 if (!request->reply->code) {
400 fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
401
402 session = request_data_reference(request, inst, 0);
403 if (!session) {
404 /*
405 * This function is called for resuming both "start" and "continue" packets, so
406 * we have to check for "start" here.
407 *
408 * We only do multi-round authentication for the ASCII authentication type.
409 * Other authentication types are defined to be one request/reply only.
410 */
411 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
412 if (!packet_is_authen_start_request(packet) ||
413 (vp && (fr_value_box_cmp(&vp->data, enum_tacacs_auth_type_ascii) != 0))) {
414 goto auth_type;
415 }
416
417 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
418 if (!vp) {
419 RDEBUG("No User-Name, replying with Authentication-GetUser");
420 request->reply->code = FR_TACACS_CODE_AUTH_GETUSER;
421 } else {
422 RDEBUG("User-Name = %pV, replying with Authentication-GetPass", &vp->data);
423 request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
424 goto add_auth_flags;
425 }
426
427 goto send_reply;
428 }
429
430 /*
431 * Last reply was "get username", we now get the password.
432 */
433 if (session->reply == FR_TACACS_CODE_AUTH_GETUSER) {
434 RDEBUG("No User-Password, replying with Authentication-GetPass");
435 request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
436
437 /*
438 * Pre-set the authentication flags reply to No-Echo
439 * RFC 8907 says this should be set when the data being
440 * requested is sensitive and should not be echoed to the
441 * user as it is being entered.
442 */
443 add_auth_flags:
446 RPEDEBUG("Failed creating Authentication-Flags attribute with No-Echo flag");
448 goto reject;
449 }
451 goto send_reply;
452 }
453
454 /*
455 * We either have a password, or the admin screwed up the configuration somehow. Just go
456 * run "Auth-Type foo".
457 */
458 goto auth_type;
459 }
460
461 /*
462 * Something set the reply code, skip
463 * the normal auth flow and respond immediately.
464 */
465 if (request->reply->code) {
466 switch (request->reply->code) {
468 RDEBUG("The 'recv Authentication-Start' section returned %s - failing the request",
469 fr_table_str_by_value(rcode_table, rcode, "<INVALID>"));
470 break;
471
473 RDEBUG("Reply packet type was set to Do-Not-Respond");
474 break;
475
476 default:
477 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
478 RDEBUG("Reply packet type was set to %s", fr_tacacs_packet_names[request->reply->code]);
479 break;
480 }
481
483 UPDATE_STATE(reply);
484
485 fr_assert(state->send != NULL);
486 return CALL_SEND_STATE(state);
487 }
488
489 /*
490 * Run authenticate foo { ... }
491 *
492 * If we can't find Auth-Type, OR if we can't find Auth-Type = foo, then it's a reject.
493 *
494 * We prefer the local Auth-Type to the Authentication-Type in the packet. But if there's no
495 * Auth-Type set by the admin, then we use what's in the packet.
496 */
497 auth_type:
498 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_auth_type);
499 if (!vp) vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
500 if (!vp) {
501 RDEBUG("No 'Auth-Type' or 'Authentication-Type' attribute found, "
502 "cannot authenticate the user - rejecting the request");
503
504 reject:
505 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
506 goto send_reply;
507 }
508
509 dv = fr_dict_enum_by_value(vp->da, &vp->data);
510 if (!dv) {
511 RDEBUG("Invalid value for '%s' attribute, cannot authenticate the user - rejecting the request",
512 vp->da->name);
513
514 goto reject;
515 }
516
517 /*
518 * The magic Auth-Type Accept value which means skip the authenticate section.
519 *
520 * And Reject means always reject. Tho the admin should just return "reject" from the section.
521 */
522 if (vp->da == attr_auth_type) {
524 request->reply->code = FR_TACACS_CODE_AUTH_PASS;
525 goto send_reply;
526
527 } else if (fr_value_box_cmp(enum_auth_type_reject, dv->value) == 0) {
528 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
529 goto send_reply;
530 }
531 }
532
533 cs = cf_section_find(inst->server_cs, "authenticate", dv->name);
534 if (!cs) {
535 RDEBUG2("No 'authenticate %s { ... }' section found - rejecting the request", dv->name);
536 goto reject;
537 }
538
539 /*
540 * Run the "authenticate foo { ... }" section.
541 *
542 * And continue with sending the generic reply.
543 */
544 return unlang_module_yield_to_section(RESULT_P, request,
545 cs, RLM_MODULE_NOOP, resume_auth_type,
546 NULL, 0, mctx->rctx);
547}
548
549RESUME(auth_type)
550{
551 static const fr_process_rcode_t auth_type_rcode = {
560 };
561
562 rlm_rcode_t rcode = RESULT_RCODE;
563 fr_process_state_t const *state;
564 fr_pair_t *vp;
565
567
569
570 /*
571 * If nothing set the reply code, then try to set it from various other things.
572 *
573 * The user could have set Authentication-Status
574 * or Packet-Type to something other than
575 * pass...
576 */
577 if (!request->reply->code) {
578 request->reply->code = reply_code(request,
580 authen_status_to_packet_code, NULL, auth_type_rcode, rcode);
581 } else {
582 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
583 }
584
585 switch (request->reply->code) {
586 case 0:
587 RDEBUG("No reply code was set. Forcing to Authentication-Fail");
588 fail:
589 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
591
592 /*
593 * Print complaints before running "send Access-Reject"
594 */
596 RDEBUG2("Failed to authenticate the user");
597 break;
598
602 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
603 if (vp && (vp->vp_uint8 != FR_AUTHENTICATION_TYPE_VALUE_ASCII)) {
604 RDEBUG2("Cannot send challenges for %pP", vp);
605 goto fail;
606 }
607 break;
608
609 default:
610 break;
611
612 }
613 UPDATE_STATE(reply);
614
615 fr_assert(state->send != NULL);
616 return state->send(p_result, mctx, request);
617}
618
619RESUME_FLAG(auth_pass, UNUSED,)
620{
622
624
625 // @todo - worry about user identity existing?
626
627 fr_state_discard(inst->auth.state_tree, request);
629}
630
631RESUME_FLAG(auth_fail, UNUSED,)
632{
634
636
637 // @todo - insert server message saying "failed"
638 // and also for FAIL
639
640 fr_state_discard(inst->auth.state_tree, request);
642}
643
644RESUME_FLAG(auth_restart, UNUSED,)
645{
647
649
650 fr_state_discard(inst->auth.state_tree, request);
652}
653
654RESUME(auth_get)
655{
658 fr_pair_t *vp, *copy;
659
661
662 /*
663 * Track multi-round authentication flows. Note that they can only start with an
664 * "Authentication-Start" packet, but they can continue with an "Authentication-Continue" packet.
665 *
666 * If there's no session being tracked, then we create one for a start packet.
667 */
668 session = request_data_reference(request, inst, 0);
669 if (!session) {
670 fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
671
673
674 MEM(session = talloc_zero(NULL, process_tacacs_session_t));
675 if (request_data_talloc_add(request, inst, 0, process_tacacs_session_t, session, true, true, true) < 0) {
676 talloc_free(session);
677 goto send_reply;
678 }
679
680 /*
681 * These are the only things which saved. The rest of the fields are either static (and statically
682 * known), or are irrelevant.
683 */
684 fr_pair_list_init(&session->list);
685#undef COPY
686#define COPY(_attr) do { \
687 vp = fr_pair_find_by_da(&request->request_pairs, NULL, _attr); \
688 if (!vp) break; \
689 MEM(copy = fr_pair_copy(session, vp)); \
690 fr_pair_append(&session->list, copy); \
691 RDEBUG2("%pP", copy); \
692} while (0)
693
694 RDEBUG2("Caching session attributes:");
695 RINDENT();
701 REXDENT();
702
703 } else {
704 /*
705 * It is possible that the user name or password are added on subsequent Authentication-Continue
706 * packets following replies with Authentication-GetUser or Authentication-GetPass.
707 * Check if they are already in the session cache, and if not, add them.
708 */
709#define COPY_MISSING(_attr) do { \
710 vp = fr_pair_find_by_da(&session->list, NULL, _attr); \
711 if (vp) break; \
712 COPY(_attr); \
713} while (0)
714
715 RDEBUG2("Caching additional session attributes:");
716 RINDENT();
719 REXDENT();
720 }
721 session->reply = request->reply->code;
722 session->seq_no = request->packet->data[2];
723
725 /*
726 * Cache the session state context.
727 */
728 if ((state_create(request->reply_ctx, &request->reply_pairs, request, true) < 0) ||
729 (fr_state_store(inst->auth.state_tree, request) < 0)) {
730 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
731 }
732
734}
735
736RECV(auth_cont)
737{
740
741 if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
742 (fr_state_restore(inst->auth.state_tree, request) < 0)) {
743 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
744 }
745
746 /*
747 * Restore key fields from the original Authentication-Start packet.
748 */
749 session = request_data_reference(request, inst, 0);
750 if (session) {
751 fr_pair_t *vp = NULL, *copy;
752
753 if (request->packet->data[2] <= session->seq_no) {
754 REDEBUG("Client sent invalid sequence number %02x, expected >%02x", request->packet->data[2], session->seq_no);
755 error:
756 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
757 }
758
759 if (fr_debug_lvl >= L_DBG_LVL_2) {
760 RDEBUG2("Restoring session attributes:");
761 RINDENT();
762 while ((vp = fr_pair_list_next(&session->list, vp))) {
763 RDEBUG2("%pP", vp);
764 }
765 REXDENT();
766 }
767 if (fr_pair_list_copy(request->request_ctx, &request->request_pairs, &session->list) < 0) goto error;
768
769 /*
770 * Copy the returned user_message into the attribute we requested.
771 */
772#define EXTRACT(_attr) \
773 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_user_message); \
774 if (!vp) break; \
775 fr_value_box_set_secret(&vp->data, _attr->flags.secret); \
776 if (pair_append_request(&copy, _attr) < 0) break; \
777 if (fr_pair_value_copy(copy, vp) < 0) { \
778 fr_pair_remove(&request->request_pairs, copy); \
779 talloc_free(copy); \
780 break; \
781 } \
782 RDEBUG2("Populated %pP from user_message", copy)
783
784 switch (session->reply) {
787 break;
788
791 break;
792
793 default:
794 break;
795 }
796 }
797
798 return CALL_RECV(generic);
799}
800
801/*
802 * The client aborted the session. The reply should be RESTART or FAIL.
803 */
804RECV(auth_cont_abort)
805{
807
808 if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
809 (fr_state_restore(inst->auth.state_tree, request) < 0)) {
810 return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
811 }
812
813 return CALL_RECV(generic);
814}
815
816RESUME(auth_cont_abort)
817{
818 rlm_rcode_t rcode = RESULT_RCODE;
819 fr_process_state_t const *state;
820
821 UPDATE_STATE(packet);
822
823 if (!request->reply->code) {
824 switch (rcode) {
825 case RLM_MODULE_OK:
827 request->reply->code = FR_TACACS_CODE_AUTH_RESTART;
828 break;
829
830 default:
831 request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
832 break;
833 }
834
835 } else {
836 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code) ||
837 (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND));
838 }
839
840 RDEBUG("Reply packet type set to %s", (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND) ? "Do-Not-Respond" : fr_tacacs_packet_names[request->reply->code]);
841
842 UPDATE_STATE(reply);
843
844 fr_assert(state->send != NULL);
845 return CALL_SEND_STATE(state);
846}
847
848
855
856
857RESUME(autz_request)
858{
859 rlm_rcode_t rcode = RESULT_RCODE;
860 fr_process_state_t const *state;
861
863
865
866 /*
867 * See if the return code from "recv" which says we reject, or continue.
868 */
869 UPDATE_STATE(packet);
870
871 /*
872 * Nothing set the reply, so let's see if we need to do so.
873 *
874 * If the admin didn't set authorization-status, just
875 * use the defaults from the state machine.
876 */
877 if (!request->reply->code) {
878 request->reply->code = reply_code(request, attr_tacacs_authorization_status,
879 author_status_to_packet_code, state, NULL, rcode);
880 if (!request->reply->code) request->reply->code = FR_TACACS_CODE_AUTZ_ERROR;
881
882 }
883 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code) ||
884 (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND));
885
886 RDEBUG("Reply packet type set to %s", (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND) ? "Do-Not-Respond" : fr_tacacs_packet_names[request->reply->code]);
887
888 UPDATE_STATE(reply);
889
890 fr_assert(state->send != NULL);
891 return CALL_SEND_STATE(state);
892}
893
898
899RESUME(acct_type)
900{
901 static const fr_process_rcode_t acct_type_rcode = {
910 };
911
912 rlm_rcode_t rcode = RESULT_RCODE;
913 fr_process_state_t const *state;
914
916
917 /*
918 * One more chance to override
919 */
920 if (!request->reply->code) {
922 NULL, acct_type_rcode, rcode);
923 if (!request->reply->code) request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
924 } else {
925 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
926 }
927
928 UPDATE_STATE(reply);
929
930 fr_assert(state->send != NULL);
931 return state->send(p_result, mctx, request);
932}
933
934static const bool acct_flag_valid[8] = {
935 false, true, true, false, /* invalid, start, stop, invalid */
936 true, true, false, false, /* watchdog - no update, watchdog - update, invalid, invalid */
937};
938
939RECV(accounting_request)
940{
941 fr_pair_t *vp;
942
943 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
944
945 /*
946 * RFC 8907 Section 7.2
947 */
948 if (vp && !acct_flag_valid[(vp->vp_uint8 & 0x0e) >> 1]) {
949 RWDEBUG("Invalid accounting request flag field %02x", vp->vp_uint8);
950 return CALL_SEND_TYPE(FR_TACACS_CODE_ACCT_ERROR);
951 }
952
953 return CALL_RECV(generic);
954}
955
956RESUME(accounting_request)
957{
958 rlm_rcode_t rcode = RESULT_RCODE;
959 fr_pair_t *vp;
960 CONF_SECTION *cs;
961 fr_dict_enum_value_t const *dv;
962 fr_process_state_t const *state;
964
966
968
969 UPDATE_STATE(packet);
970
971 /*
972 * Nothing set the reply, so let's see if we need to do so.
973 *
974 * If the admin didn't set accounting-status, just
975 * use the defaults from the state machine.
976 */
977 if (!request->reply->code) {
978 request->reply->code = reply_code(request, attr_tacacs_accounting_status,
979 acct_status_to_packet_code, state, NULL, rcode);
980 } else {
981 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
982 }
983
984 /*
985 * Something set the reply code, so we reply and don't run "accounting foo { ... }"
986 */
987 if (request->reply->code) {
988 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code) ||
989 (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND));
990
991 RDEBUG("Reply packet type set to %s", (request->reply->code == FR_TACACS_CODE_DO_NOT_RESPOND) ? "Do-Not-Respond" : fr_tacacs_packet_names[request->reply->code]);
992
993 UPDATE_STATE(reply);
994
995 fr_assert(state->send != NULL);
996 return CALL_SEND_STATE(state);
997 }
998
999 /*
1000 * Run accounting foo { ... }
1001 */
1002 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
1003 if (!vp) {
1004 fail:
1005 request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
1006 UPDATE_STATE(reply);
1007 fr_assert(state->send != NULL);
1008 return CALL_SEND_STATE(state);
1009 }
1010
1011 dv = fr_dict_enum_by_value(vp->da, &vp->data);
1012 if (!dv) goto fail;
1013
1014 cs = cf_section_find(inst->server_cs, "accounting", dv->name);
1015 if (!cs) {
1016 RDEBUG2("No 'accounting %s { ... }' section found - skipping...", dv->name);
1017 goto fail;
1018 }
1019
1020 /*
1021 * Run the "accounting foo { ... }" section.
1022 *
1023 * And continue with sending the generic reply.
1024 */
1025 return unlang_module_yield_to_section(RESULT_P, request,
1026 cs, RLM_MODULE_NOOP, resume_acct_type,
1027 NULL, 0, mctx->rctx);
1028}
1029
1030static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1031{
1032 fr_process_state_t const *state;
1033
1035
1037 fr_assert(FR_TACACS_PACKET_CODE_VALID(request->packet->code));
1038
1039 request->component = "tacacs";
1040 request->module = NULL;
1041 fr_assert(request->proto_dict == dict_tacacs);
1042
1043 UPDATE_STATE(packet);
1044
1045 if (!state->recv) {
1046 REDEBUG("Invalid packet type (%u)", request->packet->code);
1048 }
1049
1050 // @todo - debug stuff!
1051// tacacs_packet_debug(request, request->packet, &request->request_pairs, true);
1052
1053 if (unlikely(request_is_dynamic_client(request))) {
1054 return new_client(p_result, mctx, request);
1055 }
1056
1057 return state->recv(p_result, mctx, request);
1058}
1059
1061 { .required = true, .single = true, .type = FR_TYPE_OCTETS },
1063};
1064
1065/** Validates a request against a known shared secret
1066 *
1067 * Designed for the specific purpose of verifying dynamic clients
1068 * against a known shared secret in a `new client` section.
1069 *
1070 * Example:
1071@verbatim
1072%tacacs.secret.verify(<secret>)
1073@endverbatim
1074 *
1075 * @ingroup xlat_functions
1076 */
1078 request_t *request, fr_value_box_list_t *args)
1079{
1080 fr_value_box_t *secret, *vb;
1081 int ret;
1082 TALLOC_CTX *local = talloc_new(NULL);
1083 fr_pair_list_t list;
1084
1086
1087 if (request->proto_dict != dict_tacacs) return XLAT_ACTION_FAIL;
1088
1089 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
1090
1091 /*
1092 * Attempt to decode the packet using the supplied secret.
1093 * If the decoding fails then the secret is wrong.
1094 */
1095 fr_pair_list_init(&list);
1096 ret = fr_tacacs_decode(local, &list, NULL, request->packet->data, request->packet->data_len, NULL,
1097 secret->vb_strvalue, secret->vb_length, NULL);
1098 talloc_free(local);
1099
1100 if (ret < 0) {
1101 RPEDEBUG("Failed to verify the TACACS secret");
1102 vb->vb_bool = false;
1103 } else {
1104 vb->vb_bool = true;
1105 }
1107
1108 return XLAT_ACTION_DONE;
1109}
1110
1111static int mod_instantiate(module_inst_ctx_t const *mctx)
1112{
1113 process_tacacs_t *inst = talloc_get_type_abort(mctx->mi->data, process_tacacs_t);
1114
1115 inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf));
1116
1117 FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, >=, 1);
1118 FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, <=, 8);
1119
1120 FR_INTEGER_BOUND_CHECK("session.max", inst->auth.session.max_sessions, >=, 64);
1121 FR_INTEGER_BOUND_CHECK("session.max", inst->auth.session.max_sessions, <=, (1 << 18));
1122
1123 inst->auth.session.thread_safe = main_config->spawn_workers;
1124 inst->auth.session.context_id = fr_hash_string(cf_section_name2(inst->server_cs));
1125
1126 MEM(inst->auth.state_tree = fr_state_tree_init(inst, attr_tacacs_state, &inst->auth.session));
1127
1128 return 0;
1129}
1130
1131static int mod_bootstrap(module_inst_ctx_t const *mctx)
1132{
1133 CONF_SECTION *server_cs = cf_item_to_section(cf_parent(mctx->mi->conf));
1134
1135 if (virtual_server_section_attribute_define(server_cs, "authenticate", attr_auth_type) < 0) return -1;
1136
1137 return 0;
1138}
1139
1140static int mod_load(void)
1141{
1142 xlat_t *xlat;
1143
1144 if (unlikely(!(xlat = xlat_func_register(NULL, "tacacs.secret.verify", xlat_func_tacacs_secret_verify,
1145 FR_TYPE_BOOL)))) return -1;
1146
1148
1149 return 0;
1150}
1151
1152static void mod_unload(void)
1153{
1154 xlat_func_unregister("tacacs.secret.verify");
1155}
1156
1157/*
1158 * rcodes not listed under a packet_type
1159 * mean that the packet code will not be
1160 * changed.
1161 */
1162static fr_process_state_t const process_state[] = {
1163 /*
1164 * Authentication
1165 */
1167 .packet_type = {
1174 },
1175 .default_rcode = RLM_MODULE_NOOP,
1176 .recv = recv_auth_start,
1177 .resume = resume_auth_start,
1178 .section_offset = offsetof(process_tacacs_sections_t, auth_start),
1179 },
1181 .packet_type = {
1187 },
1188 .default_rcode = RLM_MODULE_NOOP,
1189 .result_rcode = RLM_MODULE_OK,
1190 .send = send_generic,
1191 .resume = resume_auth_pass,
1192 .section_offset = offsetof(process_tacacs_sections_t, auth_pass),
1193 },
1195 .packet_type = {
1201 },
1202 .default_rcode = RLM_MODULE_NOOP,
1203 .result_rcode = RLM_MODULE_REJECT,
1204 .send = send_generic,
1205 .resume = resume_auth_fail,
1206 .section_offset = offsetof(process_tacacs_sections_t, auth_fail),
1207 },
1209 .packet_type = {
1215 },
1216 .default_rcode = RLM_MODULE_NOOP,
1217 .result_rcode = RLM_MODULE_OK,
1218 .send = send_generic,
1219 .resume = resume_auth_get,
1220 .section_offset = offsetof(process_tacacs_sections_t, auth_getdata),
1221 },
1223 .packet_type = {
1229 },
1230 .default_rcode = RLM_MODULE_NOOP,
1231 .result_rcode = RLM_MODULE_OK,
1232 .send = send_generic,
1233 .resume = resume_auth_get,
1234 .section_offset = offsetof(process_tacacs_sections_t, auth_getpass),
1235 },
1237 .packet_type = {
1243 },
1244 .default_rcode = RLM_MODULE_NOOP,
1245 .result_rcode = RLM_MODULE_OK,
1246 .send = send_generic,
1247 .resume = resume_auth_get,
1248 .section_offset = offsetof(process_tacacs_sections_t, auth_getuser),
1249 },
1251 .packet_type = {
1252 },
1253 .default_rcode = RLM_MODULE_NOOP,
1254 .result_rcode = RLM_MODULE_OK,
1255 .send = send_generic,
1256 .resume = resume_auth_restart,
1257 .section_offset = offsetof(process_tacacs_sections_t, auth_restart),
1258 },
1260 .packet_type = {
1261 },
1262 .default_rcode = RLM_MODULE_NOOP,
1263 .result_rcode = RLM_MODULE_REJECT,
1264 .send = send_generic,
1265 .resume = resume_auth_restart,
1266 .section_offset = offsetof(process_tacacs_sections_t, auth_error),
1267 },
1268
1270 .packet_type = {
1277 },
1278 .default_rcode = RLM_MODULE_NOOP,
1279 .result_rcode = RLM_MODULE_OK,
1280 .recv = recv_auth_cont,
1281 .resume = resume_auth_start, /* we go back to running 'authenticate', etc. */
1282 .section_offset = offsetof(process_tacacs_sections_t, auth_cont),
1283 },
1285 .packet_type = {
1292 },
1293 .default_rcode = RLM_MODULE_NOOP,
1294 .result_rcode = RLM_MODULE_REJECT,
1295 .recv = recv_auth_cont_abort,
1296 .resume = resume_auth_cont_abort,
1297 .section_offset = offsetof(process_tacacs_sections_t, auth_cont_abort),
1298 },
1299
1300 /*
1301 * Authorization
1302 */
1304 .packet_type = {
1309
1316 },
1317 .default_rcode = RLM_MODULE_NOOP,
1318 .recv = recv_generic,
1319 .resume = resume_autz_request,
1320 .section_offset = offsetof(process_tacacs_sections_t, autz_request),
1321 },
1323 .packet_type = {
1330 },
1331 .default_rcode = RLM_MODULE_NOOP,
1332 .result_rcode = RLM_MODULE_OK,
1333 .send = send_generic,
1334 .resume = resume_send_generic,
1335 .section_offset = offsetof(process_tacacs_sections_t, autz_pass_add),
1336 },
1338 .packet_type = {
1345 },
1346 .default_rcode = RLM_MODULE_NOOP,
1347 .result_rcode = RLM_MODULE_OK,
1348 .send = send_generic,
1349 .resume = resume_send_generic,
1350 .section_offset = offsetof(process_tacacs_sections_t, autz_pass_replace),
1351 },
1353 .packet_type = {
1354 },
1355 .default_rcode = RLM_MODULE_NOOP,
1356 .result_rcode = RLM_MODULE_REJECT,
1357 .send = send_generic,
1358 .resume = resume_send_generic,
1359 .section_offset = offsetof(process_tacacs_sections_t, autz_fail),
1360 },
1362 .packet_type = {
1363 },
1364 .default_rcode = RLM_MODULE_NOOP,
1365 .result_rcode = RLM_MODULE_REJECT,
1366 .send = send_generic,
1367 .resume = resume_send_generic,
1368 .section_offset = offsetof(process_tacacs_sections_t, autz_error),
1369 },
1370
1371 /*
1372 * Accounting
1373 */
1375 .packet_type = {
1382 },
1383 .default_rcode = RLM_MODULE_NOOP,
1384 .recv = recv_accounting_request,
1385 .resume = resume_accounting_request,
1386 .section_offset = offsetof(process_tacacs_sections_t, acct_request),
1387 },
1389 .packet_type = {
1396 },
1397 .default_rcode = RLM_MODULE_NOOP,
1398 .result_rcode = RLM_MODULE_OK,
1399 .send = send_generic,
1400 .resume = resume_send_generic,
1401 .section_offset = offsetof(process_tacacs_sections_t, acct_success),
1402 },
1404 .packet_type = {
1405 },
1406 .default_rcode = RLM_MODULE_NOOP,
1407 .result_rcode = RLM_MODULE_FAIL,
1408 .send = send_generic,
1409 .resume = resume_send_generic,
1410 .section_offset = offsetof(process_tacacs_sections_t, acct_error),
1411 },
1413 .packet_type = {
1418
1425 },
1426 .default_rcode = RLM_MODULE_NOOP,
1427 .result_rcode = RLM_MODULE_HANDLED,
1428 .send = send_generic,
1429 .resume = resume_send_generic,
1430 .section_offset = offsetof(process_tacacs_sections_t, do_not_respond),
1431 }
1432};
1433
1434
1436 /**
1437 * Basically, the TACACS+ protocol use same type "authenticate" to handle
1438 * Start and Continue requests. (yep, you're right. it's horrible)
1439 * Therefore, we split the same "auth" type into two different sections just
1440 * to allow the user to have different logic for that.
1441 *
1442 * If you want to cry, just take a look at
1443 *
1444 * https://tools.ietf.org/html/rfc8907 Section 4.
1445 *
1446 * This should be an abject lesson in how NOT to design a
1447 * protocol. Pretty much everything they did was wrong.
1448 */
1449 {
1450 .section = SECTION_NAME("recv", "Authentication-Start"),
1451 .actions = &mod_actions_authenticate,
1452 .offset = PROCESS_CONF_OFFSET(auth_start),
1453 },
1454 {
1455 .section = SECTION_NAME("send", "Authentication-Pass"),
1457 .offset = PROCESS_CONF_OFFSET(auth_pass),
1458 },
1459 {
1460 .section = SECTION_NAME("send", "Authentication-Fail"),
1462 .offset = PROCESS_CONF_OFFSET(auth_fail),
1463 },
1464 {
1465 .section = SECTION_NAME("send", "Authentication-GetData"),
1467 .offset = PROCESS_CONF_OFFSET(auth_getdata),
1468 },
1469 {
1470 .section = SECTION_NAME("send", "Authentication-GetUser"),
1472 .offset = PROCESS_CONF_OFFSET(auth_getuser),
1473 },
1474 {
1475 .section = SECTION_NAME("send", "Authentication-GetPass"),
1477 .offset = PROCESS_CONF_OFFSET(auth_getpass),
1478 },
1479 {
1480 .section = SECTION_NAME("send", "Authentication-Restart"),
1482 .offset = PROCESS_CONF_OFFSET(auth_restart),
1483 },
1484 {
1485 .section = SECTION_NAME("send", "Authentication-Error"),
1487 .offset = PROCESS_CONF_OFFSET(auth_error),
1488 },
1489 {
1490 .section = SECTION_NAME("recv", "Authentication-Continue"),
1492 .offset = PROCESS_CONF_OFFSET(auth_cont),
1493 },
1494 {
1495 .section = SECTION_NAME("recv", "Authentication-Continue-Abort"),
1497 .offset = PROCESS_CONF_OFFSET(auth_cont_abort),
1498 },
1499
1500 {
1501 .section = SECTION_NAME("authenticate", CF_IDENT_ANY),
1503 },
1504
1505 /* authorization */
1506
1507 {
1508 .section = SECTION_NAME("recv", "Authorization-Request"),
1510 .offset = PROCESS_CONF_OFFSET(autz_request),
1511 },
1512 {
1513 .section = SECTION_NAME("send", "Authorization-Pass-Add"),
1515 .offset = PROCESS_CONF_OFFSET(autz_pass_add),
1516 },
1517 {
1518 .section = SECTION_NAME("send", "Authorization-Pass-Replace"),
1520 .offset = PROCESS_CONF_OFFSET(autz_pass_replace),
1521 },
1522 {
1523 .section = SECTION_NAME("send", "Authorization-Fail"),
1525 .offset = PROCESS_CONF_OFFSET(autz_fail),
1526 },
1527 {
1528 .section = SECTION_NAME("send", "Authorization-Error"),
1530 .offset = PROCESS_CONF_OFFSET(autz_error),
1531 },
1532
1533 /* accounting */
1534
1535 {
1536 .section = SECTION_NAME("recv", "Accounting-Request"),
1538 .offset = PROCESS_CONF_OFFSET(acct_request),
1539 },
1540 {
1541 .section = SECTION_NAME("send", "Accounting-Success"),
1543 .offset = PROCESS_CONF_OFFSET(acct_success),
1544 },
1545 {
1546 .section = SECTION_NAME("send", "Accounting-Error"),
1548 .offset = PROCESS_CONF_OFFSET(acct_error),
1549 },
1550
1551 {
1552 .section = SECTION_NAME("accounting", CF_IDENT_ANY),
1554 },
1555
1556 {
1557 .section = SECTION_NAME("send", "Do-Not-Respond"),
1559 .offset = PROCESS_CONF_OFFSET(do_not_respond),
1560 },
1561
1562 DYNAMIC_CLIENT_SECTIONS,
1563
1565};
1566
1567
1570 .common = {
1571 .magic = MODULE_MAGIC_INIT,
1572 .name = "tacacs",
1573 .config = config,
1575 MODULE_RCTX(process_rctx_t),
1576 .onload = mod_load,
1577 .unload = mod_unload,
1578 .bootstrap = mod_bootstrap,
1579 .instantiate = mod_instantiate
1580 },
1581 .process = mod_process,
1582 .compile_list = compile_list,
1583 .dict = &dict_tacacs,
1584 .packet_type = &attr_packet_type
1585};
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
va_list args
Definition acutest.h:770
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:529
#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:334
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition cf_parse.h:423
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1359
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:1201
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
#define cf_parent(_cf)
Definition cf_util.h:118
#define CF_IDENT_ANY
Definition cf_util.h:80
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:36
fr_value_box_t const ** out
Enumeration value.
Definition dict.h:281
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:257
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:3632
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
char const * name
Enum name.
Definition dict.h:254
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
Specifies a value which must be present for the module to function.
Definition dict.h:280
Value of an enumerated attribute.
Definition dict.h:253
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static xlat_action_t xlat_func_tacacs_secret_verify(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args)
Validates a request against a known shared secret.
Definition base.c:1077
uint64_t fr_hash64(void const *data, size_t size)
Definition hash.c:940
uint32_t fr_hash_string(char const *p)
Definition hash.c:900
uint64_t fr_hash64_update(void const *data, size_t size, uint64_t hash)
Definition hash.c:968
talloc_free(hp)
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
fr_dict_attr_t const * attr_user_name
Definition base.c:101
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:460
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
int fr_debug_lvl
Definition log.c:41
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:68
main_config_t const * main_config
Main server configuration.
Definition main_config.c:56
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_BOOL
A truth value.
@ 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:29
unlang_mod_actions_t const mod_actions_accounting
Definition mod_action.c:77
unlang_mod_actions_t const mod_actions_authorize
Definition mod_action.c:45
unlang_mod_actions_t const mod_actions_postauth
Definition mod_action.c:92
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:69
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
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:2326
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:2962
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:784
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
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:1352
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:290
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:187
static const virtual_server_compile_t compile_list[]
Definition base.c:213
static fr_process_state_t const process_state[]
Definition base.c:68
RESUME_FLAG(recv_bfd, UNUSED,)
Definition base.c:118
static fr_dict_attr_t const * attr_module_success_message
Definition base.c:36
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition base.c:399
static int mod_load(void)
Definition base.c:229
static void mod_unload(void)
Definition base.c:238
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition base.c:214
static fr_dict_attr_t const * attr_user_password
Definition base.c:59
static fr_dict_attr_t const * attr_stripped_user_name
Definition base.c:52
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition base.c:802
static fr_dict_attr_t const * attr_auth_type
Definition base.c:49
static fr_value_box_t const * enum_auth_type_reject
Definition base.c:84
static const conf_parser_t auth_config[]
Definition base.c:156
static fr_value_box_t const * enum_auth_type_accept
Definition base.c:83
static const conf_parser_t config[]
Definition base.c:162
fr_process_module_t process_tacacs
Definition base.c:1569
static fr_dict_attr_t const * attr_tacacs_authentication_flags
Definition base.c:53
CONF_SECTION * autz_pass_add
Definition base.c:144
fr_pair_list_t list
copied from the request
Definition base.c:181
CONF_SECTION * new_client
Definition base.c:155
static fr_value_box_t const * enum_tacacs_auth_type_ascii
Definition base.c:116
CONF_SECTION * add_client
Definition base.c:156
CONF_SECTION * auth_pass
Definition base.c:132
CONF_SECTION * auth_start
Definition base.c:131
CONF_SECTION * acct_success
Definition base.c:150
CONF_SECTION * auth_error
Definition base.c:138
CONF_SECTION * acct_request
Definition base.c:149
CONF_SECTION * auth_cont_abort
Definition base.c:141
fr_dict_attr_autoload_t process_tacacs_dict_attr[]
Definition base.c:77
static xlat_arg_parser_t const xlat_func_tacacs_secret_verify_args[]
Definition base.c:1060
static fr_dict_attr_t const * attr_tacacs_user_message
Definition base.c:70
CONF_SECTION * server_cs
Our virtual server.
Definition base.c:166
static fr_dict_attr_t const * attr_chap_password
Definition base.c:74
CONF_SECTION * auth_cont
Definition base.c:140
static const uint32_t acct_status_to_packet_code[UINT8_MAX+1]
Definition base.c:894
CONF_SECTION * auth_restart
Definition base.c:137
static const uint32_t authen_status_to_packet_code[UINT8_MAX+1]
Definition base.c:347
process_tacacs_sections_t sections
Pointers to various config sections we need to execute.
Definition base.c:170
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:264
static fr_dict_t const * dict_tacacs
Definition base.c:36
fr_dict_enum_autoload_t process_tacacs_dict_enum[]
Definition base.c:119
#define COPY_MISSING(_attr)
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
CONF_SECTION * autz_error
Definition base.c:147
CONF_SECTION * do_not_respond
Definition base.c:153
static const uint32_t author_status_to_packet_code[UINT8_MAX+1]
Definition base.c:849
static fr_value_box_t const * enum_auth_flags_noecho
Definition base.c:115
CONF_SECTION * autz_pass_replace
Definition base.c:145
uint32_t reply
for multiround state machine
Definition base.c:179
#define EXTRACT(_attr)
CONF_SECTION * auth_getuser
Definition base.c:135
static fr_dict_attr_t const * attr_tacacs_privilege_level
Definition base.c:64
CONF_SECTION * auth_getdata
Definition base.c:134
static fr_dict_attr_t const * attr_tacacs_authentication_type
Definition base.c:54
CONF_SECTION * acct_error
Definition base.c:151
static fr_dict_attr_t const * attr_tacacs_accounting_status
Definition base.c:59
CONF_SECTION * autz_request
Definition base.c:143
fr_state_config_t session
track state session information.
Definition base.c:161
static const bool acct_flag_valid[8]
Definition base.c:934
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
CONF_SECTION * deny_client
Definition base.c:157
CONF_SECTION * auth_fail
Definition base.c:133
static fr_dict_attr_t const * attr_tacacs_authentication_action
Definition base.c:52
static fr_dict_attr_t const * attr_tacacs_client_port
Definition base.c:62
process_tacacs_auth_t auth
Authentication configuration.
Definition base.c:173
static fr_dict_attr_t const * attr_tacacs_remote_address
Definition base.c:65
uint8_t seq_no
sequence number of last request.
Definition base.c:180
fr_state_tree_t * state_tree
State tree to link multiple requests/responses.
Definition base.c:162
static fr_dict_attr_t const * attr_tacacs_action
Definition base.c:51
static fr_dict_attr_t const * attr_tacacs_sequence_number
Definition base.c:68
static fr_dict_attr_t const * attr_tacacs_authorization_status
Definition base.c:58
CONF_SECTION * autz_fail
Definition base.c:146
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
static fr_dict_attr_t const * attr_tacacs_state
Definition base.c:69
uint32_t session_id
current session ID
Definition base.c:168
CONF_SECTION * auth_getpass
Definition base.c:136
fr_dict_autoload_t process_tacacs_dict[]
Definition base.c:39
static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
Definition base.c:211
#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.
#define COPY(_a, _b)
char const * fr_tacacs_packet_names[FR_TACACS_CODE_MAX]
Definition base.c:119
ssize_t fr_tacacs_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *vendor, uint8_t const *buffer, size_t buffer_len, const uint8_t *original, char const *const secret, size_t secret_len, int *code)
Decode a TACACS+ packet.
Definition decode.c:415
#define fr_assert(_expr)
Definition rad_assert.h:37
static char * secret
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
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:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:57
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define request_is_dynamic_client(_x)
Definition request.h:189
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:39
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
void * data
Module's instance data.
Definition module.h:293
#define MODULE_RCTX(_ctype)
Definition module.h:259
#define MODULE_INST(_ctype)
Definition module.h:257
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
int fr_state_restore(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:764
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:685
int fr_state_store(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:840
const conf_parser_t state_session_config[]
Definition state.c:59
fr_state_tree_t * fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_state_config_t const *config)
Initialise a new state tree.
Definition state.c:232
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:804
@ 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:117
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumed by an xlat function.
Definition xlat.h:145
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:748
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:4394
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
static size_t char ** out
Definition value.h:1030
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.
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:216
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition xlat_func.c:509