2 * rlm_attr_filter.c - Filter A/V Pairs received back from proxy reqs
3 * before sending reply to the NAS/Server that sent
8 * This program is is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License, version 2 if the
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright (C) 2001 The FreeRADIUS server project
22 * Copyright (C) 2001 Chris Parker <cparker@starnetusa.net>
25 #include <freeradius-devel/autoconf.h>
36 #include <freeradius-devel/radiusd.h>
37 #include <freeradius-devel/rad_assert.h>
38 #include <freeradius-devel/modules.h>
40 static const char rcsid[] = "$Id$";
43 * Define a structure with the module configuration, so it can
44 * be used as the instance handle.
46 struct attr_filter_instance {
51 static const CONF_PARSER module_config[] = {
52 { "attrsfile", PW_TYPE_FILENAME,
53 offsetof(struct attr_filter_instance,attrsfile), NULL, "${raddbdir}/attrs" },
54 { NULL, -1, 0, NULL, NULL }
57 static void check_pair(VALUE_PAIR *check_item, VALUE_PAIR *reply_item,
62 if (check_item->operator == T_OP_SET) return;
64 compare = paircmp(check_item, reply_item);
75 * Copy the specified attribute to the specified list
77 static int mypairappend(VALUE_PAIR *item, VALUE_PAIR **to)
80 tmp = paircreate(item->attribute, item->type);
82 radlog(L_ERR|L_CONS, "no memory");
89 memcpy(tmp, item, sizeof(*tmp));
96 static int getattrsfile(const char *filename, PAIR_LIST **pair_list)
99 PAIR_LIST *attrs = NULL;
103 rcode = pairlist_read(filename, &attrs, 1);
109 * Walk through the 'attrs' file list.
115 entry->check = entry->reply;
118 for (vp = entry->check; vp != NULL; vp = vp->next) {
121 * If it's NOT a vendor attribute,
122 * and it's NOT a wire protocol
123 * and we ignore Fall-Through,
124 * then bitch about it, giving a good warning message.
126 if (!(vp->attribute & ~0xffff) &&
127 (vp->attribute > 0xff) &&
128 (vp->attribute > 1000)) {
129 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
130 "\tfound in filter list for realm \"%s\".\n",
131 filename, entry->lineno, vp->name,
147 static int attr_filter_detach(void *instance)
149 struct attr_filter_instance *inst = instance;
150 pairlist_free(&inst->attrs);
151 free(inst->attrsfile);
158 * (Re-)read the "attrs" file into memory.
160 static int attr_filter_instantiate(CONF_SECTION *conf, void **instance)
162 struct attr_filter_instance *inst;
165 inst = rad_malloc(sizeof *inst);
169 memset(inst, 0, sizeof(*inst));
171 if (cf_section_parse(conf, inst, module_config) < 0) {
172 attr_filter_detach(inst);
176 rcode = getattrsfile(inst->attrsfile, &inst->attrs);
178 radlog(L_ERR|L_CONS, "Errors reading %s", inst->attrsfile);
179 attr_filter_detach(inst);
188 * Common attr_filter checks
190 static int attr_filter_common(void *instance, REQUEST *request,
193 struct attr_filter_instance *inst = instance;
195 VALUE_PAIR *output = NULL;
196 VALUE_PAIR **output_tail;
197 VALUE_PAIR *check_item;
201 VALUE_PAIR *realmpair;
202 char *realmname = NULL;
205 * Get the realm. Can't use request->config_items as
206 * that gets freed by rad_authenticate.... use the one
207 * set in the original request vps
209 realmpair = pairfind(request->packet->vps, PW_REALM);
211 /* If there is no realm...NOOP */
212 return (RLM_MODULE_NOOP);
214 realmname = realmpair->vp_strvalue;
216 output_tail = &output;
219 * Find the attr_filter profile entry for the realm.
221 for (pl = inst->attrs; pl; pl = pl->next) {
222 int fall_through = 0;
225 * If the current entry is NOT a default,
226 * AND the realm does NOT match the current entry,
227 * then skip to the next entry.
229 if ((strcmp(pl->name, "DEFAULT") != 0) &&
230 (strcmp(realmname, pl->name) != 0)) {
234 DEBUG2(" attr_filter: Matched entry %s at line %d", pl->name,
238 for (check_item = pl->check;
240 check_item = check_item->next) {
241 if (check_item->attribute == PW_FALL_THROUGH) {
247 * If it is a SET operator, add the attribute to
248 * the output list without checking it.
250 if (check_item->operator == T_OP_SET ) {
251 if (mypairappend(check_item, output_tail) < 0) {
253 return RLM_MODULE_FAIL;
255 output_tail = &((*output_tail)->next);
260 * Iterate through the input items, comparing
261 * each item to every rule, then moving it to the
262 * output list only if it matches all rules
263 * for that attribute. IE, Idle-Timeout is moved
264 * only if it matches all rules that describe an
267 for (vp = *input; vp != NULL; vp = vp->next ) {
268 /* reset the pass,fail vars for each reply item */
272 * reset the check_item pointer to
273 * beginning of the list
275 for (check_item = pl->check;
277 check_item = check_item->next) {
278 if (vp->attribute == check_item->attribute) {
279 check_pair(check_item, vp,
284 /* only move attribute if it passed all rules */
285 if (fail == 0 && pass > 0) {
286 if (mypairappend(vp, output_tail) < 0) {
288 return RLM_MODULE_FAIL;
290 output_tail = &((*output_tail)->next);
294 /* If we shouldn't fall through, break */
300 * No entry matched. We didn't do anything.
303 rad_assert(output == NULL);
304 return RLM_MODULE_NOOP;
310 return RLM_MODULE_UPDATED;
313 static int attr_filter_authorize(void *instance, REQUEST *request)
315 return attr_filter_common(instance, request, &request->packet->vps);
318 static int attr_filter_accounting(void *instance, REQUEST *request)
320 return attr_filter_common(instance, request, &request->packet->vps);
323 static int attr_filter_preproxy(void *instance, REQUEST *request)
325 return attr_filter_common(instance, request, &request->proxy->vps);
328 static int attr_filter_postproxy(void *instance, REQUEST *request)
330 return attr_filter_common(instance, request, &request->proxy_reply->vps);
333 static int attr_filter_postauth(void *instance, REQUEST *request)
335 return attr_filter_common(instance, request, &request->reply->vps);
339 /* globally exported name */
340 module_t rlm_attr_filter = {
343 0, /* type: reserved */
344 attr_filter_instantiate, /* instantiation */
345 attr_filter_detach, /* detach */
347 NULL, /* authentication */
348 attr_filter_authorize, /* authorization */
349 NULL, /* preaccounting */
350 attr_filter_accounting, /* accounting */
351 NULL, /* checksimul */
352 attr_filter_preproxy, /* pre-proxy */
353 attr_filter_postproxy, /* post-proxy */
354 attr_filter_postauth /* post-auth */