6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2002 The FreeRADIUS server project
21 * Copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr>
37 #define RLM_REGEX_INPACKET 0
38 #define RLM_REGEX_INCONFIG 1
39 #define RLM_REGEX_INREPLY 2
40 #define RLM_REGEX_INPROXY 3
41 #define RLM_REGEX_INPROXYREPLY 4
43 static const char rcsid[] = "$Id$";
45 typedef struct rlm_attr_rewrite_t {
46 char *attribute; /* The attribute to search for */
47 int attr_num; /* The attribute number */
48 char *search; /* The pattern to search for */
49 int search_len; /* The length of the search pattern */
50 char *searchin_str; /* The VALUE_PAIR list to search in. Can be either packet,reply,proxy,proxy_reply or config */
51 char searchin; /* The same as above just coded as a number for speed */
52 char *replace; /* The replacement */
53 int replace_len; /* The length of the replacement string */
54 int append; /* Switch to control append mode (1,0) */
55 int nocase; /* Ignore case */
56 int new_attr; /* Boolean. Do we create a new attribute or not? */
57 int num_matches; /* Maximum number of matches */
58 char *name; /* The module name */
62 static const CONF_PARSER module_config[] = {
63 { "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
64 { "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
65 { "searchin", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
66 { "replacewith", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
67 { "append", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,append),NULL, "no" },
68 { "ignore_case", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,nocase), NULL, "yes" },
69 { "new_attribute", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,new_attr), NULL, "no" },
70 { "max_matches", PW_TYPE_INTEGER, offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
71 { NULL, -1, 0, NULL, NULL }
75 static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
77 rlm_attr_rewrite_t *data;
79 char *instance_name = NULL;
82 * Set up a storage area for instance data
84 data = rad_malloc(sizeof(*data));
88 memset(data, 0, sizeof(*data));
91 * If the configuration parameters can't be parsed, then
94 if (cf_section_parse(conf, data, module_config) < 0) {
100 * Discover the attribute number of the key.
102 if (data->attribute == NULL) {
103 radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
106 if (data->search == NULL || data->replace == NULL) {
107 radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
110 data->search_len = strlen(data->search);
111 data->replace_len = strlen(data->replace);
113 if (data->replace_len == 0 && data->new_attr){
114 radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute.");
118 if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) {
119 radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
122 if (data->searchin_str == NULL) {
123 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
124 data->searchin = RLM_REGEX_INPACKET;
127 if (strcmp(data->searchin_str, "packet") == 0)
128 data->searchin = RLM_REGEX_INPACKET;
129 else if (strcmp(data->searchin_str, "config") == 0)
130 data->searchin = RLM_REGEX_INCONFIG;
131 else if (strcmp(data->searchin_str, "reply") == 0)
132 data->searchin = RLM_REGEX_INREPLY;
133 else if (strcmp(data->searchin_str, "proxy") == 0)
134 data->searchin = RLM_REGEX_INPROXY;
135 else if (strcmp(data->searchin_str, "proxy_reply") == 0)
136 data->searchin = RLM_REGEX_INPROXYREPLY;
138 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
139 data->searchin = RLM_REGEX_INPACKET;
141 free((char *)data->searchin_str);
143 dattr = dict_attrbyname(data->attribute);
145 radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
149 data->attr_num = dattr->attr;
150 /* Add the module instance name */
152 instance_name = cf_section_name2(conf);
153 if (instance_name != NULL)
154 data->name = strdup(instance_name);
162 static int do_attr_rewrite(void *instance, REQUEST *request)
164 rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
165 int ret = RLM_MODULE_NOOP;
166 VALUE_PAIR *attr_vp = NULL;
167 VALUE_PAIR *tmp = NULL;
169 regmatch_t pmatch[9];
173 unsigned int len = 0;
174 char err_msg[MAX_STRING_LEN];
177 unsigned int counter = 0;
178 char new_str[MAX_STRING_LEN];
180 char search_STR[MAX_STRING_LEN];
181 char replace_STR[MAX_STRING_LEN];
184 if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE)) != NULL){
185 if (data->name == NULL || strcmp(data->name,attr_vp->strvalue))
186 return RLM_MODULE_NOOP;
190 /* new_attribute = yes */
191 if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
192 DEBUG2("rlm_attr_rewrite: xlat on replace string failed.");
195 replace_len = strlen(replace_STR);
196 attr_vp = pairmake(data->attribute,replace_STR,0);
197 if (attr_vp == NULL){
198 DEBUG2("rlm_attr_rewrite: Could not add new attribute %s with value '%s'",
199 data->attribute,replace_STR);
202 switch(data->searchin){
203 case RLM_REGEX_INPACKET:
204 pairadd(&request->packet->vps,attr_vp);
206 case RLM_REGEX_INCONFIG:
207 pairadd(&request->config_items,attr_vp);
209 case RLM_REGEX_INREPLY:
210 pairadd(&request->reply->vps,attr_vp);
212 case RLM_REGEX_INPROXY:
213 if (!request->proxy) {
214 pairbasicfree(attr_vp);
215 return RLM_MODULE_NOOP;
217 pairadd(&request->proxy->vps, attr_vp);
219 case RLM_REGEX_INPROXYREPLY:
220 if (!request->proxy_reply) {
221 pairbasicfree(attr_vp);
222 return RLM_MODULE_NOOP;
224 pairadd(&request->proxy_reply->vps, attr_vp);
227 radlog(L_ERR, "rlm_attr_rewrite: Illegal value for searchin. Changing to packet.");
228 data->searchin = RLM_REGEX_INPACKET;
229 pairadd(&request->packet->vps,attr_vp);
232 DEBUG2("rlm_attr_rewrite: Added attribute %s with value '%s'",data->attribute,replace_STR);
235 /* new_attribute = no */
236 switch (data->searchin) {
237 case RLM_REGEX_INPACKET:
238 if (data->attr_num == PW_USER_NAME)
239 attr_vp = request->username;
240 else if (data->attr_num == PW_PASSWORD)
241 attr_vp = request->password;
243 tmp = request->packet->vps;
245 case RLM_REGEX_INCONFIG:
246 tmp = request->config_items;
248 case RLM_REGEX_INREPLY:
249 tmp = request->reply->vps;
251 case RLM_REGEX_INPROXYREPLY:
252 if (!request->proxy_reply)
253 return RLM_MODULE_NOOP;
254 tmp = request->proxy_reply->vps;
256 case RLM_REGEX_INPROXY:
258 return RLM_MODULE_NOOP;
259 tmp = request->proxy->vps;
262 radlog(L_ERR, "rlm_attr_rewrite: Illegal value for searchin. Changing to packet.");
263 data->searchin = RLM_REGEX_INPACKET;
264 attr_vp = pairfind(request->packet->vps, data->attr_num);
269 attr_vp = pairfind(tmp, data->attr_num);
270 if (attr_vp == NULL) {
271 DEBUG2("rlm_attr_rewrite: Could not find value pair for attribute %s",data->attribute);
274 if (attr_vp->strvalue == NULL || attr_vp->length == 0){
275 DEBUG2("rlm_attr_rewrite: Attribute %s string value NULL or of zero length",data->attribute);
278 cflags |= REG_EXTENDED;
282 if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) {
283 DEBUG2("rlm_attr_rewrite: xlat on search string failed.");
287 if ((err = regcomp(&preg,search_STR,cflags))) {
288 regerror(err, &preg, err_msg, MAX_STRING_LEN);
289 DEBUG2("rlm_attr_rewrite: regcomp() returned error: %s",err_msg);
293 ptr2 = attr_vp->strvalue;
296 for ( i = 0 ;i < (unsigned)data->num_matches; i++) {
297 err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
298 if (err == REG_NOMATCH) {
300 DEBUG2("rlm_attr_rewrite: No match found for attribute %s with value '%s'",
301 data->attribute, attr_vp->strvalue);
309 radlog(L_ERR, "rlm_attr_rewrite: match failure for attribute %s with value '%s'",
310 data->attribute, attr_vp->strvalue);
313 if (pmatch[0].rm_so == -1)
315 len = pmatch[0].rm_so;
317 len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
320 if (counter >= MAX_STRING_LEN) {
322 DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
323 data->attribute, attr_vp->strvalue);
327 strncpy(ptr, ptr2,len);
329 ptr2 += pmatch[0].rm_eo;
333 * We only run on the first match, sorry
335 for(j = 0; j <= REQUEST_MAX_REGEX; j++){
337 char buffer[sizeof(attr_vp->strvalue)];
340 * Stolen from src/main/valuepair.c, paircmp()
344 * Delete old matches if the corresponding match does not
345 * exist in the current regex
347 if (pmatch[j].rm_so == -1){
348 p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
356 attr_vp->strvalue + pmatch[j].rm_so,
357 pmatch[j].rm_eo - pmatch[j].rm_so);
358 buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
360 request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
365 if (data->replace_len != 0 &&
366 radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) {
367 DEBUG2("rlm_attr_rewrite: xlat on replace string failed.");
370 replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0;
374 counter += replace_len;
375 if (counter >= MAX_STRING_LEN) {
377 DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
378 data->attribute, attr_vp->strvalue);
382 strncpy(ptr, replace_STR, replace_len);
387 len = strlen(ptr2) + 1; /* We add the ending NULL */
389 if (counter >= MAX_STRING_LEN){
390 DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
391 data->attribute, attr_vp->strvalue);
394 strncpy(ptr, ptr2, len);
396 DEBUG2("rlm_attr_rewrite: Changed value for attribute %s from '%s' to '%s'",
397 data->attribute, attr_vp->strvalue, new_str);
398 if (pairparsevalue(attr_vp, new_str) == NULL) {
399 DEBUG2("rlm_attr_rewrite: Could not write value '%s' into attribute %s: %s", new_str, data->attribute, librad_errstr);
417 static int attr_rewrite_accounting(void *instance, REQUEST *request)
419 return do_attr_rewrite(instance, request);
422 static int attr_rewrite_authorize(void *instance, REQUEST *request)
424 return do_attr_rewrite(instance, request);
426 static int attr_rewrite_authenticate(void *instance, REQUEST *request)
428 return do_attr_rewrite(instance, request);
430 static int attr_rewrite_preacct(void *instance, REQUEST *request)
432 return do_attr_rewrite(instance, request);
434 static int attr_rewrite_ismul(void *instance, REQUEST *request)
436 return do_attr_rewrite(instance, request);
439 static int attr_rewrite_preproxy(void *instance, REQUEST *request)
441 return do_attr_rewrite(instance, request);
444 static int attr_rewrite_postproxy(void *instance, REQUEST *request)
446 return do_attr_rewrite(instance, request);
449 static int attr_rewrite_postauth(void *instance, REQUEST *request)
451 return do_attr_rewrite(instance, request);
454 static int attr_rewrite_detach(void *instance)
456 rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
459 free(data->attribute);
472 * The module name should be the only globally exported symbol.
473 * That is, everything else should be 'static'.
475 * If the module needs to temporarily modify it's instantiation
476 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
477 * The server will then take care of ensuring that the module
478 * is single-threaded.
480 module_t rlm_attr_rewrite = {
482 RLM_TYPE_THREAD_UNSAFE, /* type */
483 NULL, /* initialization */
484 attr_rewrite_instantiate, /* instantiation */
486 attr_rewrite_authenticate, /* authentication */
487 attr_rewrite_authorize, /* authorization */
488 attr_rewrite_preacct, /* preaccounting */
489 attr_rewrite_accounting, /* accounting */
490 attr_rewrite_ismul, /* checksimul */
491 attr_rewrite_preproxy, /* pre-proxy */
492 attr_rewrite_postproxy, /* post-proxy */
493 attr_rewrite_postauth /* post-auth */
495 attr_rewrite_detach, /* detach */