The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_attr_filter.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: 72413589333cbc5bcdf8576bd87256d798aee1cd $
19 * @file rlm_attr_filter.c
20 * @brief Filter the contents of a list, allowing only certain attributes.
21 *
22 * @copyright (C) 2001,2006 The FreeRADIUS server project
23 * @copyright (C) 2001 Chris Parker (cparker@starnetusa.net)
24 */
25RCSID("$Id: 72413589333cbc5bcdf8576bd87256d798aee1cd $")
26
27#define LOG_PREFIX mctx->mi->name
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/server/users_file.h>
33
34#include <sys/stat.h>
35
36#include <ctype.h>
37#include <fcntl.h>
38
39/*
40 * Define a structure with the module configuration, so it can
41 * be used as the instance handle.
42 */
43typedef struct {
44 char const *filename;
46 bool relaxed;
49
50static const conf_parser_t module_config[] = {
52 { FR_CONF_OFFSET("key", rlm_attr_filter_t, key), .dflt = "&Realm", .quote = T_BARE_WORD },
53 { FR_CONF_OFFSET("relaxed", rlm_attr_filter_t, relaxed), .dflt = "no" },
55};
56
58static fr_dict_t const *dict_radius;
59
62 { .out = &dict_freeradius, .proto = "freeradius" },
63 { .out = &dict_radius, .proto = "radius" },
64 { NULL }
65};
66
70
72
75 { .out = &attr_stripped_user_name, .name = "Stripped-User-Name", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
76 { .out = &attr_fall_through, .name = "Fall-Through", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
77 { .out = &attr_relax_filter, .name = "Relax-Filter", .type = FR_TYPE_BOOL, .dict = &dict_freeradius },
78
79 { .out = &attr_vendor_specific, .name = "Vendor-Specific", .type = FR_TYPE_VSA, .dict = &dict_radius },
80 { NULL }
81};
82
83static void check_pair(request_t *request, fr_pair_t *check_item, fr_pair_t *reply_item, int *pass, int *fail)
84{
85 int compare;
86
87 if (check_item->op == T_OP_SET) return;
88
89 compare = fr_pair_cmp(check_item, reply_item);
90 if (compare < 0) RPEDEBUG("Comparison failed");
91
92 if (compare == 1) {
93 ++*(pass);
94 } else {
95 ++*(fail);
96 }
97
98 RDEBUG3("%pP %s %pP", reply_item, compare == 1 ? "allowed by" : "disallowed by", check_item);
99
100 return;
101}
102
103static int attr_filter_getfile(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *filename, PAIR_LIST_LIST *pair_list)
104{
105 int rcode;
106 PAIR_LIST *entry = NULL;
107 map_t *map;
108
109 rcode = pairlist_read(ctx, dict_radius, filename, pair_list);
110 if (rcode < 0) {
111 return -1;
112 }
113
114 /*
115 * Walk through the 'attrs' file list.
116 */
117 while ((entry = fr_dlist_next(&pair_list->head, entry))) {
118 /*
119 * We apply the rules in the reply items.
120 */
121 if (!map_list_empty(&entry->check)) {
122 WARN("%s[%d] Check list is not empty for entry \"%s\".\n",
123 filename, entry->lineno, entry->name);
124 }
125
126 map = NULL;
127 while ((map = map_list_next(&entry->reply, map))) {
128 if (!tmpl_is_attr(map->lhs)) {
129 ERROR("%s[%d] Left side of filter %s is not an attribute",
130 filename, entry->lineno, map->lhs->name);
131 return -1;
132 }
133
134 if (tmpl_list(map->lhs) != request_attr_reply) {
135 ERROR("%s[%d] Left side of filter %s is not in the reply list",
136 filename, entry->lineno, map->lhs->name);
137 return -1;
138 }
139
140 if (fr_assignment_op[map->op]) {
141 ERROR("%s[%d] Filter %s contains invalid operator '%s'",
142 filename, entry->lineno, map->lhs->name, fr_tokens[map->op]);
143 return -1;
144 }
145
146 /*
147 * Make sure that bad things don't happen.
148 */
149 if (!map->rhs) {
150 ERROR("%s[%d] Right side of filter %s is a nested attribute - this is not (yet) supported",
151 filename, entry->lineno, map->lhs->name);
152 return -1;
153 }
154
155 if (!tmpl_is_data(map->rhs)) {
156 ERROR("%s[%d] Right side of filter %s is not a static value",
157 filename, entry->lineno, map->lhs->name);
158 return -1;
159 }
160 }
161 }
162
163 return 0;
164}
165
166/*
167 * (Re-)read the "attrs" file into memory.
168 */
169static int mod_instantiate(module_inst_ctx_t const *mctx)
170{
171 rlm_attr_filter_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_attr_filter_t);
172 int rcode;
173 pairlist_list_init(&inst->attrs);
174
175 rcode = attr_filter_getfile(inst, mctx, inst->filename, &inst->attrs);
176 if (rcode != 0) {
177 ERROR("Errors reading %s", inst->filename);
178
179 return -1;
180 }
181
182 return 0;
183}
184
185
186/*
187 * Common attr_filter checks
188 */
189static unlang_action_t CC_HINT(nonnull) attr_filter_common(TALLOC_CTX *ctx, rlm_rcode_t *p_result,
190 module_ctx_t const *mctx, request_t *request,
191 fr_pair_list_t *list)
192{
194 fr_pair_list_t output;
195 PAIR_LIST *pl = NULL;
196 int found = 0;
197 int pass, fail = 0;
198 char const *keyname = NULL;
199 char buffer[256];
200 ssize_t slen;
201
202 slen = tmpl_expand(&keyname, buffer, sizeof(buffer), request, inst->key, NULL, NULL);
203 if (slen < 0) {
205 }
206 if ((keyname == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
207 REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
209 }
210
211 /*
212 * Head of the output list
213 */
214 fr_pair_list_init(&output);
215
216 /*
217 * Find the attr_filter profile entry for the entry.
218 */
219 while ((pl = fr_dlist_next(&inst->attrs.head, pl))) {
220 int fall_through = 0;
221 int relax_filter = inst->relaxed;
222 map_t *map = NULL;
223 fr_pair_list_t tmp_list;
224 fr_pair_t *check_item, *input_item;
225 fr_pair_list_t check_list;
226
227 fr_pair_list_init(&tmp_list);
228 /*
229 * If the current entry is NOT a default,
230 * AND the realm does NOT match the current entry,
231 * then skip to the next entry.
232 */
233 if ((strcmp(pl->name, "DEFAULT") != 0) &&
234 (strcmp(keyname, pl->name) != 0)) {
235 continue;
236 }
237
238 RDEBUG2("Matched entry %s at line %d", pl->name, pl->lineno);
239 found = 1;
240
241 fr_pair_list_init(&check_list);
242
243 while ((map = map_list_next(&pl->reply, map))) {
244 if (map_to_vp(ctx, &tmp_list, request, map, NULL) < 0) {
245 RPWARN("Failed parsing map %s for check item, skipping it", map->lhs->name);
246 continue;
247 }
248
249 check_item = fr_pair_list_head(&tmp_list);
250 if (check_item->da == attr_fall_through) {
251 if (check_item->vp_uint32 == 1) {
252 fall_through = 1;
253 fr_pair_list_free(&tmp_list);
254 continue;
255 }
256 } else if (check_item->da == attr_relax_filter) {
257 relax_filter = check_item->vp_bool;
258 }
259
260 /*
261 * Remove pair from temporary list ready to
262 * add to the correct destination
263 */
264 fr_pair_remove(&tmp_list, check_item);
265
266 /*
267 * If it is a SET operator, add the attribute to
268 * the output list without checking it.
269 */
270 if (check_item->op == T_OP_SET ) {
271 fr_pair_append(&output, check_item);
272 continue;
273 }
274
275 /*
276 * Append the realized VP to the check list.
277 */
278 fr_pair_append(&check_list, check_item);
279 }
280
281 /*
282 * Iterate through the input items, comparing
283 * each item to every rule, then moving it to the
284 * output list only if it matches all rules
285 * for that attribute. IE, Idle-Timeout is moved
286 * only if it matches all rules that describe an
287 * Idle-Timeout.
288 */
289 for (input_item = fr_pair_list_head(list);
290 input_item;
291 input_item = fr_pair_list_next(list, input_item)) {
292 pass = fail = 0; /* reset the pass,fail vars for each reply item */
293
294 /*
295 * Reset the check_item pointer to beginning of the list
296 */
297 for (check_item = fr_pair_list_head(&check_list);
298 check_item;
299 check_item = fr_pair_list_next(&check_list, check_item)) {
300 /*
301 * Vendor-Specific is special, and matches any VSA if the
302 * comparison is always true.
303 */
304 if ((check_item->da == attr_vendor_specific) &&
305 (fr_dict_vendor_num_by_da(input_item->da) != 0) &&
306 (check_item->op == T_OP_CMP_TRUE)) {
307 pass++;
308 continue;
309 }
310
311 if (input_item->da == check_item->da) {
312 check_pair(request, check_item, input_item, &pass, &fail);
313 }
314 }
315
316 RDEBUG3("Attribute \"%s\" allowed by %i rules, disallowed by %i rules",
317 input_item->da->name, pass, fail);
318 /*
319 * Only move attribute if it passed all rules, or if the config says we
320 * should copy unmatched attributes ('relaxed' mode).
321 */
322 if (fail == 0 && (pass > 0 || relax_filter)) {
323 fr_pair_t *prev = fr_pair_list_prev(list, input_item);
324
325 if (!pass) {
326 RDEBUG3("Attribute \"%s\" allowed by relaxed mode", input_item->da->name);
327 }
328 fr_pair_remove(list, input_item);
329 fr_assert(input_item != NULL);
330 fr_pair_append(&output, input_item);
331 input_item = prev; /* Set input_item to previous in the list for outer loop */
332 }
333 }
334
335 /* If we shouldn't fall through, break */
336 if (!fall_through) {
337 break;
338 }
339 }
340
341 /*
342 * No entry matched. We didn't do anything.
343 */
344 if (!found) {
347 }
348
349 /*
350 * Replace the existing request list with our filtered one
351 */
352 fr_pair_list_free(list);
353 fr_pair_list_append(list, &output);
354
356}
357
358#define RLM_AF_FUNC(_x, _y) static unlang_action_t CC_HINT(nonnull) mod_##_x(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) \
359 { \
360 return attr_filter_common(request->_y##_ctx, p_result, mctx, request, &request->_y##_pairs); \
361 }
362
363RLM_AF_FUNC(request, request)
364RLM_AF_FUNC(reply, reply)
365RLM_AF_FUNC(control, control)
366RLM_AF_FUNC(session, session_state)
367
368/* globally exported name */
371 .common = {
372 .magic = MODULE_MAGIC_INIT,
373 .name = "attr_filter",
374 .inst_size = sizeof(rlm_attr_filter_t),
377 },
378 .method_group = {
379 .bindings = (module_method_binding_t[]){
380 /*
381 * Hack to support old configurations
382 */
383 { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_reply },
384 { .section = SECTION_NAME("authorize", CF_IDENT_ANY), .method = mod_request },
385
386 { .section = SECTION_NAME("recv", "accounting-request"), .method = mod_request },
387 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_request },
388
389 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_reply },
390
391 /*
392 * List name based methods
393 */
394 { .section = SECTION_NAME("request", CF_IDENT_ANY), .method = mod_request },
395 { .section = SECTION_NAME("reply", CF_IDENT_ANY), .method = mod_reply },
396 { .section = SECTION_NAME("control", CF_IDENT_ANY), .method = mod_control },
397 { .section = SECTION_NAME("session-state", CF_IDENT_ANY), .method = mod_session },
399 }
400 }
401};
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:576
#define RCSID(id)
Definition build.h:483
#define STRINGIFY(x)
Definition build.h:197
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#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
fr_token_t quote
Quoting around the default value. Only used for templates.
Definition cf_parse.h:634
#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:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:418
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Definition cf_parse.h:424
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
#define CF_IDENT_ANY
Definition cf_util.h:78
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
static uint32_t fr_dict_vendor_num_by_da(fr_dict_attr_t const *da)
Return the vendor number for an attribute.
Definition dict_ext.h:212
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RPWARN(fmt,...)
Definition log.h:301
#define RPEDEBUG(fmt,...)
Definition log.h:376
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition map.c:1487
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
long int ssize_t
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
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
int fr_pair_cmp(fr_pair_t const *a, fr_pair_t const *b)
Compare two pairs, using the operator from "a".
Definition pair.c:1969
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:1345
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
#define is_truncated(_ret, _max)
Definition print.h:48
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define WARN(fmt,...)
Definition radclient.h:47
#define RETURN_MODULE_NOOP
Definition rcode.h:62
#define RETURN_MODULE_FAIL
Definition rcode.h:56
#define RETURN_MODULE_UPDATED
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
fr_dict_attr_t const * request_attr_reply
Definition request.c:46
static fr_dict_attr_t const * attr_relax_filter
static int attr_filter_getfile(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *filename, PAIR_LIST_LIST *pair_list)
static void check_pair(request_t *request, fr_pair_t *check_item, fr_pair_t *reply_item, int *pass, int *fail)
static fr_dict_attr_t const * attr_stripped_user_name
static fr_dict_attr_t const * attr_fall_through
static fr_dict_t const * dict_freeradius
module_rlm_t rlm_attr_filter
static fr_dict_t const * dict_radius
#define RLM_AF_FUNC(_x, _y)
PAIR_LIST_LIST attrs
fr_dict_attr_autoload_t rlm_attr_filter_dict_attr[]
static fr_dict_attr_t const * attr_vendor_specific
static unlang_action_t attr_filter_common(TALLOC_CTX *ctx, rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request, fr_pair_list_t *list)
static const conf_parser_t module_config[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
fr_dict_autoload_t rlm_attr_filter_dict[]
char const * filename
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1310
static sql_fall_through_t fall_through(map_list_t *maps)
Definition rlm_sql.c:300
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
size_t inst_size
Size of the module's instance data.
Definition module.h:203
void * data
Module's instance data.
Definition module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
Named methods exported by a module.
Definition module.h:173
#define tmpl_is_attr(vpt)
Definition tmpl.h:213
static fr_dict_attr_t const * tmpl_list(tmpl_t const *vpt)
Definition tmpl.h:915
#define tmpl_is_data(vpt)
Definition tmpl.h:211
#define tmpl_expand(_out, _buff, _buff_len, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, using existing storage to hold variably sized types.
Definition tmpl.h:1060
eap_aka_sim_process_conf_t * inst
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
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 talloc_get_type_abort_const
Definition talloc.h:282
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:168
char const * fr_tokens[T_TOKEN_LAST]
Definition token.c:78
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_BARE_WORD
Definition token.h:120
@ T_OP_SET
Definition token.h:84
int pairlist_read(TALLOC_CTX *ctx, fr_dict_t const *dict, char const *file, PAIR_LIST_LIST *list)
Definition users_file.c:235
char const * name
Key for matching entry.
Definition users_file.h:39
int lineno
Line number entry read from.
Definition users_file.h:46
static void pairlist_list_init(PAIR_LIST_LIST *list)
Definition users_file.h:59
map_list_t check
List of maps for comparison / modifying control list.
Definition users_file.h:40
map_list_t reply
List of maps for modifying reply list.
Definition users_file.h:41
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
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
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:94
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
fr_pair_t * fr_pair_list_prev(fr_pair_list_t const *list, fr_pair_t const *item))
Get the previous item in a valuepair list before a specific entry.
Definition pair_inline.c:83
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
int nonnull(2, 5))