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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * Copyright (C) 2001 The FreeRADIUS server project
22 * Copyright (C) 2001 Chris Parker <cparker@starnetusa.net>
26 #include "libradius.h"
44 static const char rcsid[] = "$Id$";
46 struct attr_filter_instance {
55 * Move only the first instance of an attribute from
56 * one list to another.
58 static void mypairmove(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
60 VALUE_PAIR *to_tail, *i, *next;
61 VALUE_PAIR *iprev = NULL;
64 /* DEBUG2(" attr_filter: moving attr: %d", attr); */
67 * Find the last pair in the "to" list and put it in "to_tail".
71 for(i = *to; i; i = i->next)
76 for(i = *from; i && !moved; i = next) {
79 if (i->attribute != attr) {
85 * Remove the attribute from the "from" list.
93 * Add the attribute to the "to" list.
106 * See if a VALUE_PAIR list contains Fall-Through = Yes
108 static int fallthrough(VALUE_PAIR *vp)
112 tmp = pairfind(vp, PW_FALL_THROUGH);
114 return tmp ? tmp->lvalue : 0;
119 static CONF_PARSER module_config[] = {
120 { "attrsfile", PW_TYPE_STRING_PTR,
121 offsetof(struct attr_filter_instance,attrsfile), NULL, "${raddbdir}/attrs" },
122 { NULL, -1, 0, NULL, NULL }
125 static int getattrsfile(const char *filename, PAIR_LIST **pair_list)
128 PAIR_LIST *attrs = NULL;
132 rcode = pairlist_read(filename, &attrs, 1);
138 * Walk through the 'attrs' file list.
144 entry->check = entry->reply;
147 for (vp = entry->check; vp != NULL; vp = vp->next) {
150 * If it's NOT a vendor attribute,
151 * and it's NOT a wire protocol
152 * and we ignore Fall-Through,
153 * then bitch about it, giving a
154 * good warning message.
156 if (!(vp->attribute & ~0xffff) &&
157 (vp->attribute > 0xff) &&
158 (vp->attribute > 1000)) {
159 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
160 "\tfound in filter list for realm \"%s\".\n",
161 filename, entry->lineno, vp->name,
174 * (Re-)read the "attrs" file into memory.
176 static int attr_filter_instantiate(CONF_SECTION *conf, void **instance)
178 struct attr_filter_instance *inst;
181 inst = rad_malloc(sizeof *inst);
183 if (cf_section_parse(conf, inst, module_config) < 0) {
188 rcode = getattrsfile(inst->attrsfile, &inst->attrs);
190 radlog(L_ERR|L_CONS, "Errors reading %s", inst->attrsfile);
191 free(inst->attrsfile);
201 * Find the named realm in the database. Create the
202 * set of attribute-value pairs to check and reply with
203 * for this realm from the database.
205 static int attr_filter_authorize(void *instance, REQUEST *request)
207 struct attr_filter_instance *inst = instance;
208 VALUE_PAIR *request_pairs;
209 VALUE_PAIR **reply_items;
210 VALUE_PAIR *reply_item;
211 VALUE_PAIR *reply_tmp = NULL;
212 VALUE_PAIR *check_items;
213 VALUE_PAIR *check_item;
221 VALUE_PAIR *realmpair;
226 * It's not a proxy reply, so return NOOP
229 if( request->proxy == NULL ) {
230 return( RLM_MODULE_NOOP );
233 request_pairs = request->packet->vps;
234 reply_items = &request->reply->vps;
237 * Get the realm. Can't use request->config_items as
238 * that gets freed by rad_authenticate.... use the one
239 * set in the original request vps
241 realmpair = pairfind(request_pairs, PW_REALM);
243 /* Can't find a realm, so no filtering of attributes
244 * or should we use a DEFAULT entry?
245 * For now, just return NOTFOUND. (maybe NOOP?)
247 return RLM_MODULE_NOTFOUND;
250 realmname = (char *) realmpair->strvalue;
251 realm = realm_find(realmname);
254 * Find the attr_filter profile entry for the realm.
256 for(pl = inst->attrs; pl; pl = pl->next) {
259 * If the current entry is NOT a default,
260 * AND the realm does NOT match the current entry,
261 * then skip to the next entry.
263 if ( (strcmp(pl->name, "DEFAULT") != 0)
264 && (strcmp(realmname, pl->name) != 0) ) {
268 /* THIS SECTION NEEDS LOTS OF WORK TO GET THE ATTRIBUTE
269 * FILTERING LOGIC WORKING PROPERLY. RIGHT NOW IT DOES
270 * THINGS MOSLTY RIGHT. IT HAS SOME ISSUES WHEN YOU HAVE
271 * MULTIPLE A/V PAIRS FROM THE SAME ATTRIBUTE ( IE, VSA'S ).
272 * THAT NEEDS A BIT OF WORK STILL.... -cparker@starnetusa.net
275 DEBUG2(" attr_filter: Matched entry %s at line %d", pl->name, pl->lineno);
279 check_items = pl->check;
281 for( check_item = check_items; check_item != NULL ;
282 check_item = check_item->next ) {
285 * If it is a SET operator, add the attribute to
286 * the reply list without checking reply_items.
290 if( check_item->operator == T_OP_SET ) {
291 tmp = paircreate(check_item->attribute, check_item->type);
293 radlog(L_ERR|L_CONS, "no memory");
297 case PW_TYPE_INTEGER:
300 tmp->lvalue = check_item->lvalue;
303 strNcpy((char *)tmp->strvalue,
304 (char *)check_item->strvalue,
305 sizeof(tmp->strvalue));
306 tmp->length = check_item->length;
309 /* DEBUG2(" attr_filter: creating vp %s - %d - %d",
310 tmp->name, tmp->type, tmp->lvalue); */
311 pairadd(&reply_tmp, tmp);
315 reply_item = pairfind(*reply_items, check_item->attribute);
317 /* DEBUG2(" attr_filter: checking for: %s", check_item->name); */
319 if(reply_item != (VALUE_PAIR *)NULL) {
321 compare = simplepaircmp(request, reply_item, check_item);
323 /* DEBUG2(" attr_filter: compare = %d", compare); */
325 switch(check_item->operator) {
329 radlog(L_ERR, "Invalid operator for item %s: "
330 "reverting to '=='", check_item->name);
334 mypairmove( &reply_tmp, reply_items,
335 check_item->attribute);
341 mypairmove( &reply_tmp, reply_items,
342 check_item->attribute);
348 mypairmove( &reply_tmp, reply_items,
349 check_item->attribute);
355 mypairmove( &reply_tmp, reply_items,
356 check_item->attribute);
362 mypairmove( &reply_tmp, reply_items,
363 check_item->attribute);
369 mypairmove( &reply_tmp, reply_items,
370 check_item->attribute);
375 regcomp(®, (char *)check_item->strvalue, 0);
376 compare = regexec(®, (char *)reply_item->strvalue,
380 mypairmove( &reply_tmp, reply_items,
381 check_item->attribute);
386 regcomp(®, (char *)check_item->strvalue, 0);
387 compare = regexec(®, (char *)reply_item->strvalue,
391 mypairmove( &reply_tmp, reply_items,
392 check_item->attribute);
401 /* If we shouldn't fall through, break */
402 if(!fallthrough(pl->check))
406 pairfree(&request->reply->vps);
407 request->reply->vps = reply_tmp;
410 * See if we succeeded. If we didn't find the realm,
411 * then exit from the module.
414 return RLM_MODULE_OK;
417 * Remove server internal parameters.
419 pairdelete(reply_items, PW_FALL_THROUGH);
421 return RLM_MODULE_UPDATED;
427 static int attr_filter_detach(void *instance)
429 struct attr_filter_instance *inst = instance;
430 pairlist_free(&inst->attrs);
431 free(inst->attrsfile);
437 /* globally exported name */
438 module_t rlm_attr_filter = {
440 0, /* type: reserved */
441 NULL, /* initialization */
442 attr_filter_instantiate, /* instantiation */
444 NULL, /* authentication */
445 attr_filter_authorize, /* authorization */
446 NULL, /* preaccounting */
447 NULL, /* accounting */
448 NULL /* checksimul */
450 attr_filter_detach, /* detach */