The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 05916dc6bead34125c7e987e02cc0e771e09dba1 $
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 
27 
28 #include <freeradius-devel/io/listen.h>
29 #include <freeradius-devel/io/master.h>
30 #include <freeradius-devel/server/main_config.h>
31 #include <freeradius-devel/server/protocol.h>
32 #include <freeradius-devel/server/state.h>
33 #include <freeradius-devel/tacacs/tacacs.h>
34 #include <freeradius-devel/unlang/call.h>
35 #include <freeradius-devel/util/debug.h>
36 
37 #include <freeradius-devel/protocol/tacacs/tacacs.h>
38 
39 static fr_dict_t const *dict_freeradius;
40 static fr_dict_t const *dict_tacacs;
41 
44  { .out = &dict_freeradius, .proto = "freeradius" },
45  { .out = &dict_tacacs, .proto = "tacacs" },
46  { NULL }
47 };
48 
54 
61 
65 
75 
79 
82  { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
83  { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
84  { .out = &attr_module_success_message, .name = "Module-Success-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
85  { .out = &attr_stripped_user_name, .name = "Stripped-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
86  { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
87 
88  { .out = &attr_tacacs_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
89  { .out = &attr_tacacs_authentication_flags, .name = "Authentication-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
90  { .out = &attr_tacacs_authentication_type, .name = "Authentication-Type", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
91  { .out = &attr_tacacs_authentication_service, .name = "Authentication-Service", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
92 
93  { .out = &attr_tacacs_authentication_status, .name = "Authentication-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
94  { .out = &attr_tacacs_authorization_status, .name = "Authorization-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
95 
96  { .out = &attr_tacacs_accounting_status, .name = "Accounting-Status", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
97  { .out = &attr_tacacs_accounting_flags, .name = "Accounting-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
98 
99  { .out = &attr_tacacs_client_port, .name = "Client-Port", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
100  { .out = &attr_tacacs_data, .name = "Data", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
101  { .out = &attr_tacacs_privilege_level, .name = "Privilege-Level", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
102  { .out = &attr_tacacs_remote_address, .name = "Remote-Address", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
103  { .out = &attr_tacacs_authentication_action, .name = "Action", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
104  { .out = &attr_tacacs_session_id, .name = "Packet.Session-Id", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
105  { .out = &attr_tacacs_sequence_number, .name = "Packet.Sequence-Number", .type = FR_TYPE_UINT8, .dict = &dict_tacacs },
106  { .out = &attr_tacacs_server_message, .name = "Server-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
107  { .out = &attr_tacacs_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
108  { .out = &attr_tacacs_user_message, .name = "User-Message", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
109 
110  { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
111  { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_tacacs },
112  { .out = &attr_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs },
113 
114  { NULL }
115 };
116 
121 
124  { .out = &enum_auth_type_accept, .name = "Accept", .attr = &attr_auth_type },
125  { .out = &enum_auth_type_reject, .name = "Reject", .attr = &attr_auth_type },
126  { .out = &enum_auth_flags_noecho, .name = "No-Echo", .attr = &attr_tacacs_authentication_flags },
127  { .out = &enum_tacacs_auth_type_ascii, .name = "ASCII", .attr = &attr_tacacs_authentication_type },
128  { NULL }
129 };
130 
131 
132 typedef struct {
133  uint64_t nothing; // so that the next field isn't at offset 0
134 
143 
146 
152 
156 
159 
160 typedef struct {
161  fr_time_delta_t session_timeout; //!< Maximum time between the last response and next request.
162  uint32_t max_session; //!< Maximum ongoing session allowed.
163 
164  uint32_t max_rounds; //!< maximum number of authentication rounds allowed
165 
166  uint8_t state_server_id; //!< Sets a specific byte in the state to allow the
167  //!< authenticating server to be identified in packet
168  //!<captures.
169 
170  fr_state_tree_t *state_tree; //!< State tree to link multiple requests/responses.
172 
173 typedef struct {
174  CONF_SECTION *server_cs; //!< Our virtual server.
175 
176  uint32_t session_id; //!< current session ID
177 
178  process_tacacs_sections_t sections; //!< Pointers to various config sections
179  ///< we need to execute
180 
181  process_tacacs_auth_t auth; //!< Authentication configuration.
183 
184 typedef struct {
185  uint32_t rounds; //!< how many rounds were taken
186  uint32_t reply; //!< for multiround state machine
187  uint8_t seq_no; //!< sequence number of last request.
188  fr_pair_list_t list; //!< copied from the request
190 
191 
192 #define PROCESS_PACKET_TYPE fr_tacacs_packet_code_t
193 #define PROCESS_CODE_MAX FR_TACACS_CODE_MAX
194 #define PROCESS_PACKET_CODE_VALID FR_TACACS_PACKET_CODE_VALID
195 #define PROCESS_INST process_tacacs_t
196 
197 #include <freeradius-devel/server/process.h>
198 
199 static const conf_parser_t session_config[] = {
200  { FR_CONF_OFFSET("timeout", process_tacacs_auth_t, session_timeout), .dflt = "15" },
201  { FR_CONF_OFFSET("max", process_tacacs_auth_t, max_session), .dflt = "4096" },
202  { FR_CONF_OFFSET("max_rounds", process_tacacs_auth_t, max_rounds), .dflt = "4" },
203  { FR_CONF_OFFSET("state_server_id", process_tacacs_auth_t, state_server_id) },
204 
206 };
207 
208 static const conf_parser_t auth_config[] = {
209  { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) session_config },
210 
212 };
213 
214 static const conf_parser_t config[] = {
215  { FR_CONF_POINTER("Authentication", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) auth_config,
216  .offset = offsetof(process_tacacs_t, auth), },
217 
219 };
220 
221 
222 /*
223  * Synthesize a State attribute from connection && session information.
224  */
225 static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
226 {
227  uint8_t buffer[12];
228  uint32_t hash;
229  fr_pair_t *vp;
230 
231  vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_session_id);
232  if (!vp) return -1;
233 
234  fr_nbo_from_uint32(buffer, vp->vp_uint32);
235 
236  vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_tacacs_sequence_number);
237  if (!vp) return -1;
238 
239  /*
240  * Requests have odd sequence numbers, and replies have even sequence numbers.
241  * So if we want to synthesize a state in a reply which gets matched with the next
242  * request, we have to add 2 to it.
243  */
244  hash = vp->vp_uint8 + ((int) reply << 1);
245 
247 
248  /*
249  * Hash in the listener. For now, we don't allow internally proxied requests.
250  */
251  fr_assert(request->async != NULL);
252  fr_assert(request->async->listen != NULL);
253  hash = fr_hash(&request->async->listen, sizeof(request->async->listen));
254 
256 
258  if (!vp) return -1;
259 
260  (void) fr_pair_value_memdup(vp, buffer, 12, false);
261 
263 
264  return 0;
265 }
266 
267 /** Try and determine what the response packet type should be
268  *
269  * We check three sources:
270  * - reply.``<status_attr>``
271  * - reply.Packet-Type
272  * - State machine packet type assignments for the section rcode
273  *
274  * @param[in] request The current request.
275  * @param[in] status_da Specialised status attribute.
276  * @param[in] status2code Mapping table of *packet* status types to rcodes.
277  * @param[in] state Mappings for process state machine
278  * @param[in] process_rcode Mappings for Auth-Type / Acct-Type, which don't use the process state machine
279  * @param[in] rcode The last section rcode.
280  * @return
281  * - >0 if we determined a reply code.
282  * - 0 if we couldn't - Usually indicates additional sections should be run.
283  */
284 static uint32_t reply_code(request_t *request, fr_dict_attr_t const *status_da,
285  uint32_t const status2code[static UINT8_MAX + 1],
286  fr_process_state_t const *state, fr_process_rcode_t const process_rcode, rlm_rcode_t rcode)
287 {
288  fr_pair_t *vp;
289  uint32_t code;
290 
291  /*
292  * First check the protocol attribute for this packet type.
293  *
294  * Should be one of:
295  * - Authentication-Status
296  * - Authorization-Status
297  * - Accounting-Status
298  */
299  fr_assert(status_da->type == FR_TYPE_UINT8);
300 
301  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, status_da);
302  if (vp) {
303  code = status2code[vp->vp_uint8];
304  if (FR_TACACS_PACKET_CODE_VALID(code)) {
305  RDEBUG("Setting reply Packet-Type from %pP", vp);
306  return code;
307  }
308  REDEBUG("Ignoring invalid status %pP", vp);
309  }
310 
311  if (state) {
312  code = state->packet_type[rcode];
313  if (FR_TACACS_PACKET_CODE_VALID(code)) return code;
314  }
315 
316  if (process_rcode) {
317  code = process_rcode[rcode];
318  if (FR_TACACS_PACKET_CODE_VALID(code)) return code;
319  }
320 
321  /*
322  * Otherwise use Packet-Type (if set)
323  */
324  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
325  if (vp && FR_TACACS_PACKET_CODE_VALID(vp->vp_uint32)) {
326  RDEBUG("Setting reply Packet-Type from %pV", &vp->data);
327  return vp->vp_uint32;
328  }
329 
330  return 0;
331 }
332 
333 RECV(auth_start)
334 {
335  fr_process_state_t const *state;
336  fr_pair_t *vp;
337 
338  /*
339  * Only "Login" is supported. The others are "change password" and "sendauth", which aren't
340  * used.
341  */
342  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_action);
343  if (!vp) {
344  fail:
345  request->reply->code = FR_TACACS_CODE_AUTH_ERROR;
346  UPDATE_STATE(reply);
347 
348  fr_assert(state->send != NULL);
349  return CALL_SEND_STATE(state);
350  }
351 
352  if (vp->vp_uint8 != FR_ACTION_VALUE_LOGIN) {
353  RDEBUG("Invalid authentication action %u", vp->vp_uint8);
354  goto fail;
355  }
356 
357  /*
358  * There is no state to restore, so we just run the section as normal.
359  */
360 
361  return CALL_RECV(generic);
362 }
363 
364 RESUME(auth_type);
365 
374 };
375 
376 RESUME(auth_start)
377 {
378  rlm_rcode_t rcode = *p_result;
379  fr_pair_t *vp;
380  CONF_SECTION *cs;
381  fr_dict_enum_value_t const *dv;
382  fr_process_state_t const *state;
384 
386 
388 
389  /*
390  * See if the return code from "recv" which says we reject, or continue.
391  */
392  UPDATE_STATE(packet);
393 
394  /*
395  * Nothing set the reply, so let's see if we need to do so.
396  *
397  * If the admin didn't set authentication-status, just
398  * use the defaults from the state machine.
399  */
400  if (!request->reply->code) {
401  request->reply->code = reply_code(request,
403  authen_status_to_packet_code, state, NULL, rcode);
404  } else {
405  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
406  }
407 
408  /*
409  * Check for multi-round authentication.
410  *
411  * We only run the automatic state machine (start -> getuser -> getpass -> pass/fail)
412  * when the admin does NOT set any reply type, or any reply authentication status.
413  *
414  * However, do DO always save and restore the attributes from the start packet, so that they are
415  * visible in a later packet.
416  */
417  if (!request->reply->code) {
418  process_tacacs_session_t *session;
419  fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
420 
421  session = request_data_reference(request, inst, 0);
422  if (!session) {
423  /*
424  * This function is called for resuming both "start" and "continue" packets, so
425  * we have to check for "start" here.
426  *
427  * We only do multi-round authentication for the ASCII authentication type.
428  * Other authentication types are defined to be one request/reply only.
429  */
430  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
431  if (!packet_is_authen_start_request(packet) ||
432  (vp && (fr_value_box_cmp(&vp->data, enum_tacacs_auth_type_ascii) != 0))) {
433  goto auth_type;
434  }
435 
436  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
437  if (!vp) {
438  RDEBUG("No User-Name, replying with Authentication-GetUser");
439  request->reply->code = FR_TACACS_CODE_AUTH_GETUSER;
440  } else {
441  RDEBUG("User-Name = %pV, replying with Authentication-GetPass", &vp->data);
442  request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
443  goto add_auth_flags;
444  }
445 
446  goto send_reply;
447  }
448 
449  /*
450  * Last reply was "get username", we now get the password.
451  */
452  if (session->reply == FR_TACACS_CODE_AUTH_GETUSER) {
453  RDEBUG("No User-Password, replying with Authentication-GetPass");
454  request->reply->code = FR_TACACS_CODE_AUTH_GETPASS;
455 
456  /*
457  * Pre-set the authentication flags reply to No-Echo
458  * RFC 8907 says this should be set when the data being
459  * requested is sensitive and should not be echoed to the
460  * user as it is being entered.
461  */
462  add_auth_flags:
465  vp->data.enumv = attr_tacacs_authentication_flags;
466  goto send_reply;
467  }
468 
469  /*
470  * We either have a password, or the admin screwed up the configuration somehow. Just go
471  * run "Auth-Type foo".
472  */
473  goto auth_type;
474  }
475 
476  /*
477  * Something set the reply code, skip
478  * the normal auth flow and respond immediately.
479  */
480  if (request->reply->code) {
481  switch (request->reply->code) {
483  RDEBUG("The 'recv Authentication-Start' section returned %s - rejecting the request",
484  fr_table_str_by_value(rcode_table, rcode, "<INVALID>"));
485  break;
486 
487  default:
488  RDEBUG("Reply packet type was set to %s", fr_tacacs_packet_names[request->reply->code]);
489  break;
490  }
491 
492  send_reply:
493  UPDATE_STATE(reply);
494 
495  fr_assert(state->send != NULL);
496  return CALL_SEND_STATE(state);
497  }
498 
499  /*
500  * Run authenticate foo { ... }
501  *
502  * If we can't find Auth-Type, OR if we can't find Auth-Type = foo, then it's a reject.
503  *
504  * We prefer the local Auth-Type to the Authentication-Type in the packet. But if there's no
505  * Auth-Type set by the admin, then we use what's in the packet.
506  */
507  auth_type:
508  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_auth_type);
509  if (!vp) vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
510  if (!vp) {
511  RDEBUG("No 'Auth-Type' or 'Authentication-Type' attribute found, "
512  "cannot authenticate the user - rejecting the request");
513 
514  reject:
515  request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
516  goto send_reply;
517  }
518 
519  dv = fr_dict_enum_by_value(vp->da, &vp->data);
520  if (!dv) {
521  RDEBUG("Invalid value for '%s' attribute, cannot authenticate the user - rejecting the request",
522  vp->da->name);
523 
524  goto reject;
525  }
526 
527  /*
528  * The magic Auth-Type Accept value which means skip the authenticate section.
529  *
530  * And Reject means always reject. Tho the admin should just return "reject" from the section.
531  */
532  if (vp->da == attr_auth_type) {
534  request->reply->code = FR_TACACS_CODE_AUTH_PASS;
535  goto send_reply;
536 
537  } else if (fr_value_box_cmp(enum_auth_type_reject, dv->value) == 0) {
538  request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
539  goto send_reply;
540  }
541  }
542 
543  cs = cf_section_find(inst->server_cs, "authenticate", dv->name);
544  if (!cs) {
545  RDEBUG2("No 'authenticate %s { ... }' section found - rejecting the request", dv->name);
546  goto reject;
547  }
548 
549  /*
550  * Run the "authenticate foo { ... }" section.
551  *
552  * And continue with sending the generic reply.
553  */
554  RDEBUG("Running 'authenticate %s' from file %s", cf_section_name2(cs), cf_filename(cs));
555  return unlang_module_yield_to_section(p_result, request,
556  cs, RLM_MODULE_NOOP, resume_auth_type,
557  NULL, 0, mctx->rctx);
558 }
559 
560 RESUME(auth_type)
561 {
562  static const fr_process_rcode_t auth_type_rcode = {
571  };
572 
573  rlm_rcode_t rcode = *p_result;
574  fr_process_state_t const *state;
575  fr_pair_t *vp;
576 
578 
580 
581  /*
582  * If nothing set the reply code, then try to set it from various other things.
583  *
584  * The user could have set Authentication-Status
585  * or Packet-Type to something other than
586  * pass...
587  */
588  if (!request->reply->code) {
589  request->reply->code = reply_code(request,
591  authen_status_to_packet_code, NULL, auth_type_rcode, rcode);
592  } else {
593  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
594  }
595 
596  switch (request->reply->code) {
597  case 0:
598  RDEBUG("No reply code was set. Forcing to Authentication-Fail");
599  fail:
600  request->reply->code = FR_TACACS_CODE_AUTH_FAIL;
601  FALL_THROUGH;
602 
603  /*
604  * Print complaints before running "send Access-Reject"
605  */
607  RDEBUG2("Failed to authenticate the user");
608  break;
609 
613  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_authentication_type);
614  if (vp && (vp->vp_uint32 != FR_AUTHENTICATION_TYPE_VALUE_ASCII)) {
615  RDEBUG2("Cannot send challenges for %pP", vp);
616  goto fail;
617  }
618  break;
619 
620  default:
621  break;
622 
623  }
624  UPDATE_STATE(reply);
625 
626  fr_assert(state->send != NULL);
627  return state->send(p_result, mctx, request);
628 }
629 
630 RESUME(auth_pass)
631 {
633 
635 
636  // @todo - worry about user identity existing?
637 
638  fr_state_discard(inst->auth.state_tree, request);
640 }
641 
642 RESUME(auth_fail)
643 {
645 
647 
648  // @todo - insert server message saying "failed"
649  // and also for FAIL
650 
651  fr_state_discard(inst->auth.state_tree, request);
653 }
654 
655 RESUME(auth_restart)
656 {
658 
660 
661  fr_state_discard(inst->auth.state_tree, request);
663 }
664 
665 RESUME(auth_get)
666 {
668  process_tacacs_session_t *session;
669  fr_pair_t *vp, *copy;
670 
672 
673  /*
674  * Track multi-round authentication flows. Note that they can only start with an
675  * "Authentication-Start" packet, but they can continue with an "Authentication-Continue" packet.
676  *
677  * If there's no session being tracked, then we create one for a start packet.
678  */
679  session = request_data_reference(request, inst, 0);
680  if (!session) {
681  fr_tacacs_packet_t const *packet = (fr_tacacs_packet_t const *) request->packet->data;
682 
684 
685  MEM(session = talloc_zero(NULL, process_tacacs_session_t));
686  if (request_data_talloc_add(request, inst, 0, process_tacacs_session_t, session, true, true, true) < 0) {
687  talloc_free(session);
688  goto send_reply;
689  }
690 
691  /*
692  * These are the only things which saved. The rest of the fields are either static (and statically
693  * known), or are irrelevant.
694  */
695  fr_pair_list_init(&session->list);
696 #undef COPY
697 #define COPY(_attr) do { \
698  vp = fr_pair_find_by_da(&request->request_pairs, NULL, _attr); \
699  if (!vp) break; \
700  MEM(copy = fr_pair_copy(session, vp)); \
701  fr_pair_append(&session->list, copy); \
702  RDEBUG2("%pP", copy); \
703 } while (0)
704 
705  RDEBUG2("Caching session attributes:");
706  RINDENT();
712  REXDENT();
713 
714  } else {
715  session->rounds++;
716 
717  if (session->rounds > inst->auth.max_rounds) {
718  REDEBUG("Too many rounds of authentication - failing the session");
719  return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_FAIL);
720  }
721 
722  /*
723  * It is possible that the user name or password are added on subsequent Authentication-Continue
724  * packets following replies with Authentication-GetUser or Authentication-GetPass.
725  * Check if they are already in the session cache, and if not, add them.
726  */
727 #define COPY_MISSING(_attr) do { \
728  vp = fr_pair_find_by_da(&session->list, NULL, _attr); \
729  if (vp) break; \
730  COPY(_attr); \
731 } while (0)
732 
733  RDEBUG2("Caching additional session attributes:");
734  RINDENT();
737  REXDENT();
738  }
739  session->reply = request->reply->code;
740  session->seq_no = request->packet->data[2];
741 
742 send_reply:
743  /*
744  * Cache the session state context.
745  */
746  if ((state_create(request->reply_ctx, &request->reply_pairs, request, true) < 0) ||
747  (fr_request_to_state(inst->auth.state_tree, request) < 0)) {
748  return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
749  }
750 
752 }
753 
754 RECV(auth_cont)
755 {
757  process_tacacs_session_t *session;
758 
759  if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
760  (fr_state_to_request(inst->auth.state_tree, request) < 0)) {
761  return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
762  }
763 
764  /*
765  * Restore key fields from the original Authentication-Start packet.
766  */
767  session = request_data_reference(request, inst, 0);
768  if (session) {
769  fr_pair_t *vp = NULL, *copy;
770 
771  if (request->packet->data[2] <= session->seq_no) {
772  REDEBUG("Client sent invalid sequence number %02x, expected >%02x", request->packet->data[2], session->seq_no);
773  error:
774  return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
775  }
776 
777  if (fr_debug_lvl >= L_DBG_LVL_2) {
778  RDEBUG2("Restoring session attributes:");
779  RINDENT();
780  while ((vp = fr_pair_list_next(&session->list, vp))) {
781  RDEBUG2("%pP", vp);
782  }
783  REXDENT();
784  }
785  if (fr_pair_list_copy(request->request_ctx, &request->request_pairs, &session->list) < 0) goto error;
786 
787  /*
788  * Copy the returned user_message into the attribute we requested.
789  */
790 #define EXTRACT(_attr) \
791  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_user_message); \
792  if (!vp) break; \
793  fr_value_box_set_secret(&vp->data, _attr->flags.secret); \
794  if (pair_append_request(&copy, _attr) < 0) break; \
795  if (fr_pair_value_copy(copy, vp) < 0) { \
796  fr_pair_remove(&request->request_pairs, copy); \
797  talloc_free(copy); \
798  break; \
799  } \
800  RDEBUG2("Populated %pP from user_message", copy)
801 
802  switch (session->reply) {
805  break;
806 
809  break;
810 
811  default:
812  break;
813  }
814  }
815 
816  return CALL_RECV(generic);
817 }
818 
819 /*
820  * The client aborted the session. The reply should be RESTART or FAIL.
821  */
822 RECV(auth_cont_abort)
823 {
825 
826  if ((state_create(request->request_ctx, &request->request_pairs, request, false) < 0) ||
827  (fr_state_to_request(inst->auth.state_tree, request) < 0)) {
828  return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_ERROR);
829  }
830 
831  return CALL_RECV(generic);
832 }
833 
834 RESUME(auth_cont_abort)
835 {
836  fr_process_state_t const *state;
837 
838  if (!request->reply->code) request->reply->code = FR_TACACS_CODE_AUTH_RESTART;
839 
840  UPDATE_STATE(reply);
841 
842  fr_assert(state->send != NULL);
843  return CALL_SEND_STATE(state);
844 }
845 
846 
852 };
853 
854 
855 RESUME(autz_request)
856 {
857  rlm_rcode_t rcode = *p_result;
858  fr_process_state_t const *state;
859 
861 
863 
864  /*
865  * See if the return code from "recv" which says we reject, or continue.
866  */
867  UPDATE_STATE(packet);
868 
869  /*
870  * Nothing set the reply, so let's see if we need to do so.
871  *
872  * If the admin didn't set authorization-status, just
873  * use the defaults from the state machine.
874  */
875  if (!request->reply->code) {
876  request->reply->code = reply_code(request, attr_tacacs_authorization_status,
877  author_status_to_packet_code, state, NULL, rcode);
878  if (!request->reply->code) request->reply->code = FR_TACACS_CODE_AUTZ_ERROR;
879 
880  } else {
881  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
882  }
883 
884  RDEBUG("Reply packet type set to %s", fr_tacacs_packet_names[request->reply->code]);
885 
886  UPDATE_STATE(reply);
887 
888  fr_assert(state->send != NULL);
889  return CALL_SEND_STATE(state);
890 }
891 
895 };
896 
897 RESUME(acct_type)
898 {
899  static const fr_process_rcode_t acct_type_rcode = {
908  };
909 
910  rlm_rcode_t rcode = *p_result;
911  fr_process_state_t const *state;
912 
914 
915  /*
916  * One more chance to override
917  */
918  if (!request->reply->code) {
920  NULL, acct_type_rcode, rcode);
921  if (!request->reply->code) request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
922  } else {
923  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
924  }
925 
926  UPDATE_STATE(reply);
927 
928  fr_assert(state->send != NULL);
929  return state->send(p_result, mctx, request);
930 }
931 
932 static const bool acct_flag_valid[8] = {
933  false, true, true, false, /* invalid, start, stop, invalid */
934  true, true, false, false, /* watchdog - no update, watchdog - update, invalid, invalid */
935 };
936 
937 RECV(accounting_request)
938 {
939  fr_pair_t *vp;
940 
941  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
942 
943  /*
944  * RFC 8907 Section 7.2
945  */
946  if (vp && !acct_flag_valid[(vp->vp_uint8 & 0x0e) >> 1]) {
947  RWDEBUG("Invalid accounting request flag field %02x", vp->vp_uint8);
948  return CALL_SEND_TYPE(FR_TACACS_CODE_ACCT_ERROR);
949  }
950 
951  return CALL_RECV(generic);
952 }
953 
954 RESUME(accounting_request)
955 {
956  rlm_rcode_t rcode = *p_result;
957  fr_pair_t *vp;
958  CONF_SECTION *cs;
959  fr_dict_enum_value_t const *dv;
960  fr_process_state_t const *state;
962 
964 
966 
967  UPDATE_STATE(packet);
968 
969  /*
970  * Nothing set the reply, so let's see if we need to do so.
971  *
972  * If the admin didn't set accounting-status, just
973  * use the defaults from the state machine.
974  */
975  if (!request->reply->code) {
976  request->reply->code = reply_code(request, attr_tacacs_accounting_status,
977  acct_status_to_packet_code, state, NULL, rcode);
978  } else {
979  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->reply->code));
980  }
981 
982  /*
983  * Something set the reply code, so we reply and don't run "accounting foo { ... }"
984  */
985  if (request->reply->code) {
986  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->packet->code));
987 
988  RDEBUG("Reply packet type was set to %s", fr_tacacs_packet_names[request->reply->code]);
989 
990  UPDATE_STATE(reply);
991 
992  fr_assert(state->send != NULL);
993  return CALL_SEND_STATE(state);
994  }
995 
996  /*
997  * Run accounting foo { ... }
998  */
999  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_tacacs_accounting_flags);
1000  if (!vp) {
1001  fail:
1002  request->reply->code = FR_TACACS_CODE_ACCT_ERROR;
1003  UPDATE_STATE(reply);
1004  fr_assert(state->send != NULL);
1005  return CALL_SEND_STATE(state);
1006  }
1007 
1008  dv = fr_dict_enum_by_value(vp->da, &vp->data);
1009  if (!dv) goto fail;
1010 
1011  cs = cf_section_find(inst->server_cs, "accounting", dv->name);
1012  if (!cs) {
1013  RDEBUG2("No 'accounting %s { ... }' section found - skipping...", dv->name);
1014  goto fail;
1015  }
1016 
1017  /*
1018  * Run the "accounting foo { ... }" section.
1019  *
1020  * And continue with sending the generic reply.
1021  */
1022  return unlang_module_yield_to_section(p_result, request,
1023  cs, RLM_MODULE_NOOP, resume_acct_type,
1024  NULL, 0, mctx->rctx);
1025 }
1026 
1027 static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
1028 {
1029  fr_process_state_t const *state;
1030 
1031  PROCESS_TRACE;
1032 
1034  fr_assert(FR_TACACS_PACKET_CODE_VALID(request->packet->code));
1035 
1036  request->component = "tacacs";
1037  request->module = NULL;
1038  fr_assert(request->dict == dict_tacacs);
1039 
1040  UPDATE_STATE(packet);
1041 
1042  if (!state->recv) {
1043  REDEBUG("Invalid packet type (%u)", request->packet->code);
1045  }
1046 
1047  // @todo - debug stuff!
1048 // tacacs_packet_debug(request, request->packet, &request->request_pairs, true);
1049 
1050  return state->recv(p_result, mctx, request);
1051 }
1052 
1053 
1054 static int mod_instantiate(module_inst_ctx_t const *mctx)
1055 {
1056  process_tacacs_t *inst = talloc_get_type_abort(mctx->inst->data, process_tacacs_t);
1057 
1058  inst->auth.state_tree = fr_state_tree_init(inst, attr_tacacs_state, main_config->spawn_workers, inst->auth.max_session,
1059  inst->auth.session_timeout, inst->auth.state_server_id,
1060  fr_hash_string(cf_section_name2(inst->server_cs)));
1061  return 0;
1062 }
1063 
1064 static int mod_bootstrap(module_inst_ctx_t const *mctx)
1065 {
1066  process_tacacs_t *inst = talloc_get_type_abort(mctx->inst->data, process_tacacs_t);
1067 
1068  inst->server_cs = cf_item_to_section(cf_parent(mctx->inst->conf));
1069  if (virtual_server_section_attribute_define(inst->server_cs, "authenticate", attr_auth_type) < 0) return -1;
1070 
1071  FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, >=, 1);
1072  FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, <=, 8);
1073 
1074  FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, >=, 64);
1075  FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, <=, (1 << 18));
1076 
1077  return 0;
1078 }
1079 
1080 /*
1081  * rcodes not listed under a packet_type
1082  * mean that the packet code will not be
1083  * changed.
1084  */
1085 static fr_process_state_t const process_state[] = {
1086  /*
1087  * Authentication
1088  */
1090  .packet_type = {
1096  },
1097  .rcode = RLM_MODULE_NOOP,
1098  .recv = recv_auth_start,
1099  .resume = resume_auth_start,
1100  .section_offset = offsetof(process_tacacs_sections_t, auth_start),
1101  },
1102  [ FR_TACACS_CODE_AUTH_PASS ] = {
1103  .packet_type = {
1108  },
1109  .rcode = RLM_MODULE_NOOP,
1110  .send = send_generic,
1111  .resume = resume_auth_pass,
1112  .section_offset = offsetof(process_tacacs_sections_t, auth_pass),
1113  },
1114  [ FR_TACACS_CODE_AUTH_FAIL ] = {
1115  .packet_type = {
1120  },
1121  .rcode = RLM_MODULE_NOOP,
1122  .send = send_generic,
1123  .resume = resume_auth_fail,
1124  .section_offset = offsetof(process_tacacs_sections_t, auth_fail),
1125  },
1127  .packet_type = {
1132  },
1133  .rcode = RLM_MODULE_NOOP,
1134  .send = send_generic,
1135  .resume = resume_auth_get,
1136  .section_offset = offsetof(process_tacacs_sections_t, auth_getdata),
1137  },
1139  .packet_type = {
1144  },
1145  .rcode = RLM_MODULE_NOOP,
1146  .send = send_generic,
1147  .resume = resume_auth_get,
1148  .section_offset = offsetof(process_tacacs_sections_t, auth_getpass),
1149  },
1151  .packet_type = {
1156  },
1157  .rcode = RLM_MODULE_NOOP,
1158  .send = send_generic,
1159  .resume = resume_auth_get,
1160  .section_offset = offsetof(process_tacacs_sections_t, auth_getuser),
1161  },
1163  .packet_type = {
1164  },
1165  .rcode = RLM_MODULE_NOOP,
1166  .send = send_generic,
1167  .resume = resume_auth_restart,
1168  .section_offset = offsetof(process_tacacs_sections_t, auth_restart),
1169  },
1171  .packet_type = {
1172  },
1173  .rcode = RLM_MODULE_NOOP,
1174  .send = send_generic,
1175  .resume = resume_auth_restart,
1176  .section_offset = offsetof(process_tacacs_sections_t, auth_error),
1177  },
1178 
1179  [ FR_TACACS_CODE_AUTH_CONT ] = {
1180  .packet_type = {
1186  },
1187  .rcode = RLM_MODULE_NOOP,
1188  .recv = recv_auth_cont,
1189  .resume = resume_auth_start, /* we go back to running 'authenticate', etc. */
1190  .section_offset = offsetof(process_tacacs_sections_t, auth_cont),
1191  },
1193  .packet_type = {
1199  },
1200  .rcode = RLM_MODULE_NOOP,
1201  .recv = recv_auth_cont_abort,
1202  .resume = resume_auth_cont_abort,
1203  .section_offset = offsetof(process_tacacs_sections_t, auth_cont_abort),
1204  },
1205 
1206  /*
1207  * Authorization
1208  */
1210  .packet_type = {
1215 
1221  },
1222  .rcode = RLM_MODULE_NOOP,
1223  .recv = recv_generic,
1224  .resume = resume_autz_request,
1225  .section_offset = offsetof(process_tacacs_sections_t, autz_request),
1226  },
1228  .packet_type = {
1234  },
1235  .rcode = RLM_MODULE_NOOP,
1236  .send = send_generic,
1237  .resume = resume_send_generic,
1238  .section_offset = offsetof(process_tacacs_sections_t, autz_pass_add),
1239  },
1241  .packet_type = {
1247  },
1248  .rcode = RLM_MODULE_NOOP,
1249  .send = send_generic,
1250  .resume = resume_send_generic,
1251  .section_offset = offsetof(process_tacacs_sections_t, autz_pass_replace),
1252  },
1253  [ FR_TACACS_CODE_AUTZ_FAIL ] = {
1254  .packet_type = {
1255  },
1256  .rcode = RLM_MODULE_NOOP,
1257  .send = send_generic,
1258  .resume = resume_send_generic,
1259  .section_offset = offsetof(process_tacacs_sections_t, autz_fail),
1260  },
1262  .packet_type = {
1263  },
1264  .rcode = RLM_MODULE_NOOP,
1265  .send = send_generic,
1266  .resume = resume_send_generic,
1267  .section_offset = offsetof(process_tacacs_sections_t, autz_error),
1268  },
1269 
1270  /*
1271  * Accounting
1272  */
1274  .packet_type = {
1280  },
1281  .rcode = RLM_MODULE_NOOP,
1282  .recv = recv_accounting_request,
1283  .resume = resume_accounting_request,
1284  .section_offset = offsetof(process_tacacs_sections_t, acct_request),
1285  },
1287  .packet_type = {
1293  },
1294  .rcode = RLM_MODULE_NOOP,
1295  .send = send_generic,
1296  .resume = resume_send_generic,
1297  .section_offset = offsetof(process_tacacs_sections_t, acct_success),
1298  },
1300  .packet_type = {
1301  },
1302  .rcode = RLM_MODULE_NOOP,
1303  .send = send_generic,
1304  .resume = resume_send_generic,
1305  .section_offset = offsetof(process_tacacs_sections_t, acct_error),
1306  },
1307 };
1308 
1309 
1311  /**
1312  * Basically, the TACACS+ protocol use same type "authenticate" to handle
1313  * Start and Continue requests. (yep, you're right. it's horrible)
1314  * Therefore, we split the same "auth" type into two different sections just
1315  * to allow the user to have different logic for that.
1316  *
1317  * If you want to cry, just take a look at
1318  *
1319  * https://tools.ietf.org/html/rfc8907 Section 4.
1320  *
1321  * This should be an abject lesson in how NOT to design a
1322  * protocol. Pretty much everything they did was wrong.
1323  */
1324  {
1325  .name = "recv",
1326  .name2 = "Authentication-Start",
1327  .component = MOD_AUTHENTICATE,
1328  .offset = PROCESS_CONF_OFFSET(auth_start),
1329  },
1330  {
1331  .name = "send",
1332  .name2 = "Authentication-Pass",
1333  .component = MOD_POST_AUTH,
1334  .offset = PROCESS_CONF_OFFSET(auth_pass),
1335  },
1336  {
1337  .name = "send",
1338  .name2 = "Authentication-Fail",
1339  .component = MOD_POST_AUTH,
1340  .offset = PROCESS_CONF_OFFSET(auth_fail),
1341  },
1342  {
1343  .name = "send",
1344  .name2 = "Authentication-GetData",
1345  .component = MOD_POST_AUTH,
1346  .offset = PROCESS_CONF_OFFSET(auth_getdata),
1347  },
1348  {
1349  .name = "send",
1350  .name2 = "Authentication-GetUser",
1351  .component = MOD_POST_AUTH,
1352  .offset = PROCESS_CONF_OFFSET(auth_getuser),
1353  },
1354  {
1355  .name = "send",
1356  .name2 = "Authentication-GetPass",
1357  .component = MOD_POST_AUTH,
1358  .offset = PROCESS_CONF_OFFSET(auth_getpass),
1359  },
1360  {
1361  .name = "send",
1362  .name2 = "Authentication-Restart",
1363  .component = MOD_POST_AUTH,
1364  .offset = PROCESS_CONF_OFFSET(auth_restart),
1365  },
1366  {
1367  .name = "send",
1368  .name2 = "Authentication-Error",
1369  .component = MOD_POST_AUTH,
1370  .offset = PROCESS_CONF_OFFSET(auth_error),
1371  },
1372  {
1373  .name = "recv",
1374  .name2 = "Authentication-Continue",
1375  .component = MOD_AUTHENTICATE,
1376  .offset = PROCESS_CONF_OFFSET(auth_cont),
1377  },
1378  {
1379  .name = "recv",
1380  .name2 = "Authentication-Continue-Abort",
1381  .component = MOD_AUTHENTICATE,
1382  .offset = PROCESS_CONF_OFFSET(auth_cont_abort),
1383  },
1384 
1385  {
1386  .name = "authenticate",
1387  .name2 = CF_IDENT_ANY,
1388  .component = MOD_AUTHENTICATE,
1389  },
1390 
1391  /* authorization */
1392 
1393  {
1394  .name = "recv",
1395  .name2 = "Authorization-Request",
1396  .component = MOD_AUTHORIZE,
1397  .offset = PROCESS_CONF_OFFSET(autz_request),
1398  },
1399  {
1400  .name = "send",
1401  .name2 = "Authorization-Pass-Add",
1402  .component = MOD_POST_AUTH,
1403  .offset = PROCESS_CONF_OFFSET(autz_pass_add),
1404  },
1405  {
1406  .name = "send",
1407  .name2 = "Authorization-Pass-Replace",
1408  .component = MOD_POST_AUTH,
1409  .offset = PROCESS_CONF_OFFSET(autz_pass_replace),
1410  },
1411  {
1412  .name = "send",
1413  .name2 = "Authorization-Fail",
1414  .component = MOD_POST_AUTH,
1415  .offset = PROCESS_CONF_OFFSET(autz_fail),
1416  },
1417  {
1418  .name = "send",
1419  .name2 = "Authorization-Error",
1420  .component = MOD_POST_AUTH,
1421  .offset = PROCESS_CONF_OFFSET(autz_error),
1422  },
1423 
1424  /* accounting */
1425 
1426  {
1427  .name = "recv",
1428  .name2 = "Accounting-Request",
1429  .component = MOD_ACCOUNTING,
1430  .offset = PROCESS_CONF_OFFSET(acct_request),
1431  },
1432  {
1433  .name = "send",
1434  .name2 = "Accounting-Success",
1435  .component = MOD_POST_AUTH,
1436  .offset = PROCESS_CONF_OFFSET(acct_success),
1437  },
1438  {
1439  .name = "send",
1440  .name2 = "Accounting-Error",
1441  .component = MOD_POST_AUTH,
1442  .offset = PROCESS_CONF_OFFSET(acct_error),
1443  },
1444 
1445  {
1446  .name = "accounting",
1447  .name2 = CF_IDENT_ANY,
1448  .component = MOD_ACCOUNTING,
1449  },
1450 
1451  {
1452  .name = "send",
1453  .name2 = "Do-Not-Respond",
1454  .component = MOD_POST_AUTH,
1455  .offset = PROCESS_CONF_OFFSET(do_not_respond),
1456  },
1457 
1459 };
1460 
1461 
1464  .common = {
1465  .magic = MODULE_MAGIC_INIT,
1466  .name = "tacacs",
1467  .config = config,
1468  .inst_size = sizeof(process_tacacs_t),
1469  .bootstrap = mod_bootstrap,
1471  },
1472  .process = mod_process,
1473  .compile_list = compile_list,
1474  .dict = &dict_tacacs,
1475 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char char buffer[256]
Definition: acutest.h:574
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition: cf_parse.h:486
#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:268
#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:310
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:400
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
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:970
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:649
#define cf_parent(_cf)
Definition: cf_util.h:98
#define cf_filename(_cf)
Definition: cf_util.h:104
#define CF_IDENT_ANY
Definition: cf_util.h:78
@ MOD_POST_AUTH
7 methods index for postauth section.
Definition: components.h:37
@ MOD_AUTHORIZE
1 methods index for authorize section.
Definition: components.h:34
@ MOD_ACCOUNTING
3 methods index for accounting section.
Definition: components.h:36
@ MOD_AUTHENTICATE
0 methods index for authenticate section.
Definition: components.h:33
fr_value_box_t const ** out
Enumeration value.
Definition: dict.h:239
fr_dict_enum_value_t * 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:2946
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
fr_value_box_t const * value
Enum value (what name maps to).
Definition: dict.h:213
char const * name
Enum name.
Definition: dict.h:210
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Specifies a value which must be present for the module to function.
Definition: dict.h:238
Value of an enumerated attribute.
Definition: dict.h:209
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
uint32_t fr_hash(void const *data, size_t size)
Definition: hash.c:806
uint32_t fr_hash_string(char const *p)
Definition: hash.c:859
fr_dict_t const * dict_freeradius
Definition: base.c:73
fr_dict_attr_t const * attr_user_name
Definition: base.c:97
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:42
@ 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:69
bool spawn_workers
Should the server spawn threads.
Definition: main_config.h:58
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
#define UINT8_MAX
Definition: merged_model.c:32
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
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:51
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
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
fr_pair_t * fr_pair_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:278
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:2316
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:2978
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:1340
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition: pair.c:46
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
RESUME(check_yiaddr)
Definition: base.c:132
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition: base.c:415
fr_process_module_t process_tacacs
Definition: base.c:1463
static fr_dict_attr_t const * attr_tacacs_authentication_flags
Definition: base.c:57
CONF_SECTION * autz_pass_add
Definition: base.c:148
static fr_dict_attr_t const * attr_packet_type
Definition: base.c:53
fr_pair_list_t list
copied from the request
Definition: base.c:188
static fr_dict_attr_t const * attr_user_password
Definition: base.c:77
static virtual_server_compile_t compile_list[]
Definition: base.c:1310
static fr_dict_attr_t const * attr_module_failure_message
Definition: base.c:50
static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: base.c:1027
static fr_value_box_t const * enum_tacacs_auth_type_ascii
Definition: base.c:120
CONF_SECTION * auth_pass
Definition: base.c:136
CONF_SECTION * auth_start
Definition: base.c:135
CONF_SECTION * acct_success
Definition: base.c:154
static fr_dict_attr_t const * attr_stripped_user_name
Definition: base.c:52
CONF_SECTION * auth_error
Definition: base.c:142
CONF_SECTION * acct_request
Definition: base.c:153
CONF_SECTION * auth_cont_abort
Definition: base.c:145
fr_dict_attr_autoload_t process_tacacs_dict_attr[]
Definition: base.c:81
static fr_dict_attr_t const * attr_tacacs_user_message
Definition: base.c:74
CONF_SECTION * server_cs
Our virtual server.
Definition: base.c:174
static fr_dict_attr_t const * attr_chap_password
Definition: base.c:78
static const conf_parser_t session_config[]
Definition: base.c:199
CONF_SECTION * auth_cont
Definition: base.c:144
static const uint32_t acct_status_to_packet_code[UINT8_MAX+1]
Definition: base.c:892
CONF_SECTION * auth_restart
Definition: base.c:141
static const uint32_t authen_status_to_packet_code[UINT8_MAX+1]
Definition: base.c:366
process_tacacs_sections_t sections
Pointers to various config sections we need to execute.
Definition: base.c:178
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:284
static fr_dict_t const * dict_tacacs
Definition: base.c:40
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition: base.c:1064
fr_time_delta_t session_timeout
Maximum time between the last response and next request.
Definition: base.c:161
uint32_t rounds
how many rounds were taken
Definition: base.c:185
static fr_dict_attr_t const * attr_auth_type
Definition: base.c:49
fr_dict_enum_autoload_t process_tacacs_dict_enum[]
Definition: base.c:123
#define COPY_MISSING(_attr)
static fr_dict_attr_t const * attr_tacacs_session_id
Definition: base.c:71
static fr_dict_attr_t const * attr_tacacs_server_message
Definition: base.c:70
CONF_SECTION * autz_error
Definition: base.c:151
CONF_SECTION * do_not_respond
Definition: base.c:157
static fr_value_box_t const * enum_auth_type_reject
Definition: base.c:118
static const conf_parser_t auth_config[]
Definition: base.c:208
static const uint32_t author_status_to_packet_code[UINT8_MAX+1]
Definition: base.c:847
static fr_process_state_t const process_state[]
Definition: base.c:1085
static fr_value_box_t const * enum_auth_flags_noecho
Definition: base.c:119
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:166
CONF_SECTION * autz_pass_replace
Definition: base.c:149
uint32_t reply
for multiround state machine
Definition: base.c:186
#define EXTRACT(_attr)
static fr_value_box_t const * enum_auth_type_accept
Definition: base.c:117
CONF_SECTION * auth_getuser
Definition: base.c:139
static fr_dict_attr_t const * attr_tacacs_privilege_level
Definition: base.c:68
CONF_SECTION * auth_getdata
Definition: base.c:138
static fr_dict_attr_t const * attr_tacacs_authentication_type
Definition: base.c:58
CONF_SECTION * acct_error
Definition: base.c:155
static fr_dict_attr_t const * attr_tacacs_accounting_status
Definition: base.c:63
CONF_SECTION * autz_request
Definition: base.c:147
uint32_t max_session
Maximum ongoing session allowed.
Definition: base.c:162
static const bool acct_flag_valid[8]
Definition: base.c:932
static fr_dict_attr_t const * attr_tacacs_authentication_service
Definition: base.c:59
static fr_dict_attr_t const * attr_tacacs_authentication_status
Definition: base.c:60
CONF_SECTION * auth_fail
Definition: base.c:137
static fr_dict_attr_t const * attr_tacacs_authentication_action
Definition: base.c:56
static fr_dict_attr_t const * attr_tacacs_client_port
Definition: base.c:66
process_tacacs_auth_t auth
Authentication configuration.
Definition: base.c:181
static fr_dict_attr_t const * attr_tacacs_remote_address
Definition: base.c:69
uint8_t seq_no
sequence number of last request.
Definition: base.c:187
fr_state_tree_t * state_tree
State tree to link multiple requests/responses.
Definition: base.c:170
#define COPY(_attr)
static fr_dict_attr_t const * attr_tacacs_action
Definition: base.c:55
static const conf_parser_t config[]
Definition: base.c:214
static fr_dict_attr_t const * attr_tacacs_sequence_number
Definition: base.c:72
static fr_dict_attr_t const * attr_tacacs_authorization_status
Definition: base.c:62
CONF_SECTION * autz_fail
Definition: base.c:150
static fr_dict_attr_t const * attr_tacacs_accounting_flags
Definition: base.c:64
static fr_dict_attr_t const * attr_tacacs_data
Definition: base.c:67
static fr_dict_attr_t const * attr_tacacs_state
Definition: base.c:73
uint32_t max_rounds
maximum number of authentication rounds allowed
Definition: base.c:164
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: base.c:1054
static fr_dict_attr_t const * attr_module_success_message
Definition: base.c:51
uint32_t session_id
current session ID
Definition: base.c:176
CONF_SECTION * auth_getpass
Definition: base.c:140
fr_dict_autoload_t process_tacacs_dict[]
Definition: base.c:43
static int state_create(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, bool reply)
Definition: base.c:225
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition: process.h:65
module_t common
Common fields for all loadable modules.
Definition: process.h:55
Common public symbol definition for all process modules.
Definition: process.h:54
char const * fr_tacacs_packet_names[FR_TACACS_CODE_MAX]
Definition: base.c:119
#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)
Definition: radius1_test.c:190
fr_table_num_sorted_t const rcode_table[]
Definition: rcode.c:35
#define RETURN_MODULE_OK
Definition: rcode.h:57
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:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition: rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition: rcode.h:50
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition: rcode.h:44
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
Definition: request_data.c:339
#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.
Definition: request_data.h:86
static unsigned int hash(char const *username, unsigned int tablesize)
Definition: rlm_passwd.c:132
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
#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:222
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:606
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:737
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:660
unlang_action_t unlang_module_yield_to_section(rlm_rcode_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:516
RETURN_MODULE_FAIL
if(!subtype_vp) goto fail
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
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:253
@ 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:321
#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_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:270
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:70
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition: value.c:640
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:3689
static size_t char ** out
Definition: value.h:984
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.
#define COMPILE_TERMINATOR
char const * name
Name of the processing section, such as "recv" or "send".
Processing sections which are allowed in this virtual server.