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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2002,2006 The FreeRADIUS server project
21 * Copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
34 #define RLM_REGEX_INPACKET 0
35 #define RLM_REGEX_INCONFIG 1
36 #define RLM_REGEX_INREPLY 2
37 #define RLM_REGEX_INPROXY 3
38 #define RLM_REGEX_INPROXYREPLY 4
40 typedef struct rlm_attr_rewrite_t {
41 char *attribute; /* The attribute to search for */
42 DICT_ATTR *da; /* The attribute definition */
43 char *search; /* The pattern to search for */
44 int search_len; /* The length of the search pattern */
45 char *searchin_str; /* The VALUE_PAIR list to search in. Can be either packet,reply,proxy,proxy_reply or control (plus it's alias 'config') */
46 char searchin; /* The same as above just coded as a number for speed */
47 char *replace; /* The replacement */
48 int replace_len; /* The length of the replacement string */
49 int append; /* Switch to control append mode (1,0) */
50 int nocase; /* Ignore case */
51 int new_attr; /* Boolean. Do we create a new attribute or not? */
52 int num_matches; /* Maximum number of matches */
53 const char *name; /* The module name */
56 static const CONF_PARSER module_config[] = {
57 { "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
58 { "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
59 { "searchin", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
60 { "replacewith", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
61 { "append", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,append),NULL, "no" },
62 { "ignore_case", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,nocase), NULL, "yes" },
63 { "new_attribute", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,new_attr), NULL, "no" },
64 { "max_matches", PW_TYPE_INTEGER, offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
65 { NULL, -1, 0, NULL, NULL }
68 static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
70 rlm_attr_rewrite_t *data;
74 * Set up a storage area for instance data
76 data = rad_malloc(sizeof(*data));
80 memset(data, 0, sizeof(*data));
83 * If the configuration parameters can't be parsed, then
86 if (cf_section_parse(conf, data, module_config) < 0) {
92 * Discover the attribute number of the key.
94 if (data->attribute == NULL) {
95 radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
98 if (data->search == NULL || data->replace == NULL) {
99 radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
102 data->search_len = strlen(data->search);
103 data->replace_len = strlen(data->replace);
105 if (data->replace_len == 0 && data->new_attr){
106 radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute.");
110 if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) {
111 radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
114 if (data->searchin_str == NULL) {
115 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
116 data->searchin = RLM_REGEX_INPACKET;
119 if (strcmp(data->searchin_str, "packet") == 0)
120 data->searchin = RLM_REGEX_INPACKET;
121 else if (strcmp(data->searchin_str, "config") == 0)
122 data->searchin = RLM_REGEX_INCONFIG;
123 else if (strcmp(data->searchin_str, "control") == 0)
124 data->searchin = RLM_REGEX_INCONFIG;
125 else if (strcmp(data->searchin_str, "reply") == 0)
126 data->searchin = RLM_REGEX_INREPLY;
128 else if (strcmp(data->searchin_str, "proxy") == 0)
129 data->searchin = RLM_REGEX_INPROXY;
130 else if (strcmp(data->searchin_str, "proxy_reply") == 0)
131 data->searchin = RLM_REGEX_INPROXYREPLY;
134 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
135 data->searchin = RLM_REGEX_INPACKET;
138 dattr = dict_attrbyname(data->attribute);
140 radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
145 /* Add the module instance name */
146 data->name = cf_section_name2(conf); /* may be NULL */
153 static int do_attr_rewrite(void *instance, REQUEST *request)
155 rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
156 int ret = RLM_MODULE_NOOP;
157 VALUE_PAIR *attr_vp = NULL;
158 VALUE_PAIR *tmp = NULL;
160 regmatch_t pmatch[9];
164 unsigned int len = 0;
165 char err_msg[MAX_STRING_LEN];
168 unsigned int counter = 0;
169 char new_str[MAX_STRING_LEN];
171 char search_STR[MAX_STRING_LEN];
172 char replace_STR[MAX_STRING_LEN];
174 if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE, 0)) != NULL){
175 if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue))
176 return RLM_MODULE_NOOP;
180 /* new_attribute = yes */
181 if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
182 DEBUG2("%s: xlat on replace string failed.", data->name);
185 attr_vp = pairmake(data->attribute,replace_STR,0);
186 if (attr_vp == NULL){
187 DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name,
188 data->attribute,replace_STR);
191 switch(data->searchin){
192 case RLM_REGEX_INPACKET:
193 pairadd(&request->packet->vps,attr_vp);
195 case RLM_REGEX_INCONFIG:
196 pairadd(&request->config_items,attr_vp);
198 case RLM_REGEX_INREPLY:
199 pairadd(&request->reply->vps,attr_vp);
202 case RLM_REGEX_INPROXY:
203 if (!request->proxy) {
204 pairbasicfree(attr_vp);
205 return RLM_MODULE_NOOP;
207 pairadd(&request->proxy->vps, attr_vp);
209 case RLM_REGEX_INPROXYREPLY:
210 if (!request->proxy_reply) {
211 pairbasicfree(attr_vp);
212 return RLM_MODULE_NOOP;
214 pairadd(&request->proxy_reply->vps, attr_vp);
218 radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
219 data->searchin = RLM_REGEX_INPACKET;
220 pairadd(&request->packet->vps,attr_vp);
223 DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR);
228 /* new_attribute = no */
229 switch (data->searchin) {
230 case RLM_REGEX_INPACKET:
231 if (!data->da->vendor && (data->da->attr == PW_USER_NAME))
232 attr_vp = request->username;
233 else if (!data->da->vendor && (data->da->attr == PW_USER_PASSWORD))
234 attr_vp = request->password;
236 tmp = request->packet->vps;
238 case RLM_REGEX_INCONFIG:
239 tmp = request->config_items;
241 case RLM_REGEX_INREPLY:
242 tmp = request->reply->vps;
245 case RLM_REGEX_INPROXYREPLY:
246 if (!request->proxy_reply)
247 return RLM_MODULE_NOOP;
248 tmp = request->proxy_reply->vps;
250 case RLM_REGEX_INPROXY:
252 return RLM_MODULE_NOOP;
253 tmp = request->proxy->vps;
257 radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
258 data->searchin = RLM_REGEX_INPACKET;
259 attr_vp = pairfind(request->packet->vps, data->da->attr, data->da->vendor);
264 attr_vp = pairfind(tmp, data->da->attr, data->da->vendor);
265 if (attr_vp == NULL) {
266 DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute);
269 if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){
270 DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute);
273 cflags |= REG_EXTENDED;
277 if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) {
278 DEBUG2("%s: xlat on search string failed.", data->name);
282 if ((err = regcomp(&preg,search_STR,cflags))) {
283 regerror(err, &preg, err_msg, MAX_STRING_LEN);
284 DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg);
288 if ((attr_vp->type == PW_TYPE_IPADDR) &&
289 (attr_vp->vp_strvalue[0] == '\0')) {
290 inet_ntop(AF_INET, &(attr_vp->vp_ipaddr),
291 attr_vp->vp_strvalue,
292 sizeof(attr_vp->vp_strvalue));
296 ptr2 = attr_vp->vp_strvalue;
299 for ( i = 0 ;i < (unsigned)data->num_matches; i++) {
300 err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
301 if (err == REG_NOMATCH) {
303 DEBUG2("%s: Does not match: %s = %s", data->name,
304 data->attribute, attr_vp->vp_strvalue);
312 radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name,
313 data->attribute, attr_vp->vp_strvalue);
316 if (pmatch[0].rm_so == -1)
318 len = pmatch[0].rm_so;
320 len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
323 if (counter >= MAX_STRING_LEN) {
325 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
326 data->attribute, attr_vp->vp_strvalue);
330 memcpy(ptr, ptr2,len);
333 ptr2 += pmatch[0].rm_eo;
337 * We only run on the first match, sorry
339 for(j = 0; j <= REQUEST_MAX_REGEX; j++){
341 char buffer[sizeof(attr_vp->vp_strvalue)];
344 * Stolen from src/main/valuepair.c, paircompare()
348 * Delete old matches if the corresponding match does not
349 * exist in the current regex
351 if (pmatch[j].rm_so == -1){
352 p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
360 attr_vp->vp_strvalue + pmatch[j].rm_so,
361 pmatch[j].rm_eo - pmatch[j].rm_so);
362 buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
364 request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
369 if (data->replace_len != 0 &&
370 radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) {
371 DEBUG2("%s: xlat on replace string failed.", data->name);
374 replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0;
378 counter += replace_len;
379 if (counter >= MAX_STRING_LEN) {
381 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
382 data->attribute, attr_vp->vp_strvalue);
386 memcpy(ptr, replace_STR, replace_len);
392 len = strlen(ptr2) + 1; /* We add the ending NULL */
394 if (counter >= MAX_STRING_LEN){
395 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
396 data->attribute, attr_vp->vp_strvalue);
399 memcpy(ptr, ptr2, len);
402 DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name,
403 data->attribute, attr_vp->vp_strvalue, new_str);
404 if (pairparsevalue(attr_vp, new_str) == NULL) {
405 DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror());
422 static int attr_rewrite_accounting(void *instance, REQUEST *request)
424 return do_attr_rewrite(instance, request);
427 static int attr_rewrite_authorize(void *instance, REQUEST *request)
429 return do_attr_rewrite(instance, request);
432 static int attr_rewrite_authenticate(void *instance, REQUEST *request)
434 return do_attr_rewrite(instance, request);
437 static int attr_rewrite_preacct(void *instance, REQUEST *request)
439 return do_attr_rewrite(instance, request);
442 static int attr_rewrite_checksimul(void *instance, REQUEST *request)
444 return do_attr_rewrite(instance, request);
448 static int attr_rewrite_preproxy(void *instance, REQUEST *request)
450 return do_attr_rewrite(instance, request);
453 static int attr_rewrite_postproxy(void *instance, REQUEST *request)
455 return do_attr_rewrite(instance, request);
459 static int attr_rewrite_postauth(void *instance, REQUEST *request)
461 return do_attr_rewrite(instance, request);
464 static int attr_rewrite_detach(void *instance)
471 * The module name should be the only globally exported symbol.
472 * That is, everything else should be 'static'.
474 * If the module needs to temporarily modify it's instantiation
475 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
476 * The server will then take care of ensuring that the module
477 * is single-threaded.
479 module_t rlm_attr_rewrite = {
482 RLM_TYPE_THREAD_UNSAFE, /* type */
483 attr_rewrite_instantiate, /* instantiation */
484 attr_rewrite_detach, /* detach */
486 attr_rewrite_authenticate, /* authentication */
487 attr_rewrite_authorize, /* authorization */
488 attr_rewrite_preacct, /* preaccounting */
489 attr_rewrite_accounting, /* accounting */
490 attr_rewrite_checksimul, /* checksimul */
492 attr_rewrite_preproxy, /* pre-proxy */
493 attr_rewrite_postproxy, /* post-proxy */
497 attr_rewrite_postauth /* post-auth */