The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_tacacs.c
Go to the documentation of this file.
1/*
2 * This program is 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 (at
5 * 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: 5263e22e8c8613614abb99787666b92fe6c04de8 $
19 * @file rlm_tacacs.c
20 * @brief A TACACS client library.
21 *
22 * @copyright 2023 Network RADIUS SAS (legal@networkradius.com)
23 */
24#include "lib/unlang/action.h"
25RCSID("$Id: 5263e22e8c8613614abb99787666b92fe6c04de8 $")
26
27#include <freeradius-devel/io/application.h>
28#include <freeradius-devel/server/modpriv.h>
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/util/dlist.h>
31
32#include "rlm_tacacs.h"
33
34static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
35
36/*
37 * Retransmission intervals for the packets we support.
38 */
40 { FR_CONF_OFFSET("initial_rtx_time", fr_retry_config_t, irt), .dflt = STRINGIFY(2) },
41 { FR_CONF_OFFSET("max_rtx_time", fr_retry_config_t, mrt), .dflt = STRINGIFY(16) },
42 { FR_CONF_OFFSET("max_rtx_count", fr_retry_config_t, mrc), .dflt = STRINGIFY(5) },
43 { FR_CONF_OFFSET("max_rtx_duration", fr_retry_config_t, mrd), .dflt = STRINGIFY(30) },
45};
46
47/*
48 * A mapping of configuration file names to internal variables.
49 */
50static conf_parser_t const module_config[] = {
51 { FR_CONF_OFFSET_TYPE_FLAGS("transport", FR_TYPE_VOID, 0, rlm_tacacs_t, io_submodule),
53
55 .func = type_parse },
56
57 { FR_CONF_OFFSET("max_attributes", rlm_tacacs_t, max_attributes), .dflt = STRINGIFY(FR_MAX_ATTRIBUTES) },
58
59 { FR_CONF_OFFSET("response_window", rlm_tacacs_t, response_window), .dflt = STRINGIFY(20) },
60
61 { FR_CONF_OFFSET("zombie_period", rlm_tacacs_t, zombie_period), .dflt = STRINGIFY(40) },
62
63 { FR_CONF_OFFSET("revive_interval", rlm_tacacs_t, revive_interval) },
64
65 { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_tacacs_t, trunk_conf, trunk_config ) },
66
67 { FR_CONF_OFFSET_SUBSECTION("retry", 0, rlm_tacacs_t, retry, retry_config ) },
68
70};
71
72static fr_dict_t const *dict_tacacs;
73
76 { .out = &dict_tacacs, .proto = "tacacs" },
77 { NULL }
78};
79
81
84 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_tacacs },
85 { NULL }
86};
87
88/** Set which types of packets we can parse
89 *
90 * @param[in] ctx to allocate data in (instance of rlm_tacacs).
91 * @param[out] out Where to write the parsed data.
92 * @param[in] parent Base structure address.
93 * @param[in] ci #CONF_PAIR specifying the name of the type module.
94 * @param[in] rule unused.
95 * @return
96 * - 0 on success.
97 * - -1 on failure.
98 */
99static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
100 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
101{
102 char const *type_str = cf_pair_value(cf_item_to_pair(ci));
103 fr_dict_enum_value_t const *type_enum;
104 uint32_t code;
105
106#ifndef NDEBUG
108#endif
109
110 /*
111 * Must be the TACACS+ module
112 */
113 fr_assert(cs && (strcmp(cf_section_name1(cs), "tacacs") == 0));
114
115 /*
116 * Allow the process module to be specified by
117 * packet type.
118 */
119 type_enum = fr_dict_enum_by_name(attr_packet_type, type_str, -1);
120 if (!type_enum) {
121 cf_log_err(ci, "Unknown TACACS+ packet type '%s'", type_str);
122 return -1;
123 }
124
125 code = type_enum->value->vb_uint32;
126
127 memcpy(out, &code, sizeof(code));
128
129 return 0;
130}
131
132static void mod_tacacs_signal(module_ctx_t const *mctx, request_t *request, fr_signal_t action)
133{
135 rlm_tacacs_io_t const *io = (rlm_tacacs_io_t const *)inst->io_submodule->exported; /* Public symbol exported by the module */
136
137 /*
138 * We received a duplicate packet, ignore the dup, and rely on the
139 * IO submodule / trunk to do it's own retransmissions.
140 */
141 if (action == FR_SIGNAL_DUP) return;
142
143 if (!io->signal) return;
144
145 io->signal(MODULE_CTX(inst->io_submodule,
146 module_thread(inst->io_submodule)->data, mctx->env_data,
147 mctx->rctx), request, action);
148}
149
150/** Send packets outbound.
151 *
152 */
153static unlang_action_t CC_HINT(nonnull) mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
154{
157
158 void *rctx = NULL;
159
160 if (!FR_TACACS_PACKET_CODE_VALID(request->packet->code)) {
161 REDEBUG("Invalid packet code %d", request->packet->code);
163 }
164
165 if (!inst->allowed[request->packet->code]) {
166 REDEBUG("Packet code %s is disallowed by the configuration",
167 fr_tacacs_packet_names[request->packet->code]);
169 }
170
171 /*
172 * Push the request and it's data to the IO submodule.
173 *
174 * This may return YIELD, for "please yield", or it may
175 * return another code which indicates what happened to
176 * the request...
177 */
178 ua = inst->io->enqueue(p_result, &rctx, inst->io_submodule->data,
179 module_thread(inst->io_submodule)->data, request);
180 if (ua != UNLANG_ACTION_YIELD) {
181 fr_assert(rctx == NULL);
183 }
184
185 return unlang_module_yield(request, inst->io->resume, mod_tacacs_signal, 0, rctx);
186}
187
188static int mod_instantiate(module_inst_ctx_t const *mctx)
189{
190 size_t i, num_types;
191 rlm_tacacs_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_tacacs_t);
192
193 inst->io = (rlm_tacacs_io_t const *)inst->io_submodule->exported; /* Public symbol exported by the module */
194 inst->name = mctx->mi->name;
195
196 /*
197 * These limits are specific to TACACS, and cannot be over-ridden, due to 8-bit ID fields!
198 */
199 FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2);
200 FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255);
201 FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2);
202
203 FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, >=, fr_time_delta_from_sec(1));
204 FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, <=, fr_time_delta_from_sec(120));
205
206 FR_TIME_DELTA_BOUND_CHECK("zombie_period", inst->zombie_period, >=, fr_time_delta_from_sec(1));
207 FR_TIME_DELTA_BOUND_CHECK("zombie_period", inst->zombie_period, <=, fr_time_delta_from_sec(120));
208
209 FR_TIME_DELTA_BOUND_CHECK("revive_interval", inst->revive_interval, >=, fr_time_delta_from_sec(10));
210 FR_TIME_DELTA_BOUND_CHECK("revive_interval", inst->revive_interval, <=, fr_time_delta_from_sec(3600));
211
212 num_types = talloc_array_length(inst->types);
213 fr_assert(num_types > 0);
214
215 /*
216 * Allow for O(1) lookup later...
217 */
218 for (i = 0; i < num_types; i++) {
219 uint32_t code;
220
221 code = inst->types[i];
223
224 inst->allowed[code] = true;
225 }
226
227
228 return 0;
229}
230
231static int mod_load(void)
232{
233 if (fr_tacacs_global_init() < 0) {
234 PERROR("Failed initialising protocol library");
235 return -1;
236 }
237 return 0;
238}
239
240static void mod_unload(void)
241{
243}
244
245/*
246 * The module name should be the only globally exported symbol.
247 * That is, everything else should be 'static'.
248 *
249 * If the module needs to temporarily modify it's instantiation
250 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
251 * The server will then take care of ensuring that the module
252 * is single-threaded.
253 */
256 .common = {
257 .magic = MODULE_MAGIC_INIT,
258 .name = "tacacs",
259 .inst_size = sizeof(rlm_tacacs_t),
261 .dict = &dict_tacacs,
262
263 .onload = mod_load,
264 .unload = mod_unload,
265
267 },
268 .method_group = {
269 .bindings = (module_method_binding_t[]){
270 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process },
272 }
273 }
274};
Unlang interpreter actions.
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
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
#define RCSID(id)
Definition build.h:485
#define STRINGIFY(x)
Definition build.h:197
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:612
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:518
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:272
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:313
#define FR_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:529
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:434
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:448
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:449
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1593
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_parent(_cf)
Definition cf_util.h:101
#define CF_IDENT_ANY
Definition cf_util.h:78
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:274
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:287
fr_value_box_t const * value
Enum value (what name maps to).
Definition dict.h:237
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3439
Specifies an attribute which must be present for the module to function.
Definition dict.h:273
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:286
Value of an enumerated attribute.
Definition dict.h:233
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define PERROR(_fmt,...)
Definition log.h:228
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
unsigned int uint32_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
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 module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic conf_parser_t func for loading drivers.
Definition module_rlm.c:936
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:186
char const * fr_tacacs_packet_names[FR_TACACS_CODE_MAX]
Definition base.c:119
void fr_tacacs_global_free(void)
Definition base.c:167
int fr_tacacs_global_init(void)
Definition base.c:144
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RETURN_UNLANG_FAIL
Definition rcode.h:57
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1297
static int mod_load(void)
Definition rlm_tacacs.c:231
static fr_dict_attr_t const * attr_packet_type
Definition rlm_tacacs.c:80
fr_dict_attr_autoload_t rlm_tacacs_dict_attr[]
Definition rlm_tacacs.c:83
static conf_parser_t retry_config[]
Definition rlm_tacacs.c:39
static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
static fr_dict_t const * dict_tacacs
Definition rlm_tacacs.c:72
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Send packets outbound.
Definition rlm_tacacs.c:153
fr_dict_autoload_t rlm_tacacs_dict[]
Definition rlm_tacacs.c:75
static void mod_unload(void)
Definition rlm_tacacs.c:240
module_rlm_t rlm_tacacs
Definition rlm_tacacs.c:255
static void mod_tacacs_signal(module_ctx_t const *mctx, request_t *request, fr_signal_t action)
Definition rlm_tacacs.c:132
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_tacacs.c:188
static conf_parser_t const module_config[]
Definition rlm_tacacs.c:50
struct rlm_tacacs_s rlm_tacacs_t
Definition rlm_tacacs.h:36
unlang_module_signal_t signal
Send a signal to an IO module.
Definition rlm_tacacs.h:76
Public structure describing an I/O path for an outgoing socket.
Definition rlm_tacacs.h:73
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name
Instance name e.g. user_database.
Definition module.h:355
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
void * data
Thread specific instance data.
Definition module.h:372
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:501
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_DUP
A duplicate request was received.
Definition signal.h:44
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:434
eap_aka_sim_process_conf_t * inst
#define FR_TACACS_PACKET_CODE_VALID(_code)
Definition tacacs.h:322
#define FR_MAX_ATTRIBUTES
Definition tacacs.h:28
#define talloc_get_type_abort_const
Definition talloc.h:287
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
conf_parser_t const trunk_config[]
Config parser definitions to populate a trunk_conf_t.
Definition trunk.c:314
static fr_slen_t parent
Definition pair.h:839
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1020