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, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 * @file rlm_attr_rewrite.c
19 * @brief Rewrite attribute values.
21 * @copyright 2001,2006 The FreeRADIUS server project
22 * @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 {
41 char *attribute; //!< The attribute to search for.
42 const 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.
46 //!< Can be either packet, reply, proxy,
47 //!< proxy_reply or control (plus it's alias
49 char searchin; //!< The same as above just coded as a number
51 char *replace; //!< The replacement.
52 int replace_len; //!< The length of the replacement string.
53 int append; //!< Switch to control append mode (1,0).
54 int nocase; //!< Ignore case.
55 int new_attr; //!< Boolean. Whether we need to create a new
57 int num_matches; //!< Maximum number of matches.
58 const char *name; //!< The module name.
61 static const CONF_PARSER module_config[] = {
62 { "attribute", PW_TYPE_STRING_PTR,
63 offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
64 { "searchfor", PW_TYPE_STRING_PTR,
65 offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
66 { "searchin", PW_TYPE_STRING_PTR,
67 offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
68 { "replacewith", PW_TYPE_STRING_PTR,
69 offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
70 { "append", PW_TYPE_BOOLEAN,
71 offsetof(rlm_attr_rewrite_t,append),NULL, "no" },
72 { "ignore_case", PW_TYPE_BOOLEAN,
73 offsetof(rlm_attr_rewrite_t,nocase), NULL, "yes" },
74 { "new_attribute", PW_TYPE_BOOLEAN,
75 offsetof(rlm_attr_rewrite_t,new_attr), NULL, "no" },
76 { "max_matches", PW_TYPE_INTEGER,
77 offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
78 { NULL, -1, 0, NULL, NULL }
81 static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
83 rlm_attr_rewrite_t *inst;
84 const DICT_ATTR *dattr;
87 * Set up a storage area for instance data
89 *instance = inst = talloc_zero(conf, rlm_attr_rewrite_t);
95 * If the configuration parameters can't be parsed, then
98 if (cf_section_parse(conf, inst, module_config) < 0) {
103 * Discover the attribute number of the key.
105 if (inst->attribute == NULL) {
106 radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
109 if (inst->search == NULL || inst->replace == NULL) {
110 radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
113 inst->search_len = strlen(inst->search);
114 inst->replace_len = strlen(inst->replace);
116 if (inst->replace_len == 0 && inst->new_attr){
117 radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute.");
121 if (inst->num_matches < 1 || inst->num_matches > MAX_STRING_LEN) {
122 radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
125 if (inst->searchin_str == NULL) {
126 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
127 inst->searchin = RLM_REGEX_INPACKET;
130 if (strcmp(inst->searchin_str, "packet") == 0)
131 inst->searchin = RLM_REGEX_INPACKET;
132 else if (strcmp(inst->searchin_str, "config") == 0)
133 inst->searchin = RLM_REGEX_INCONFIG;
134 else if (strcmp(inst->searchin_str, "control") == 0)
135 inst->searchin = RLM_REGEX_INCONFIG;
136 else if (strcmp(inst->searchin_str, "reply") == 0)
137 inst->searchin = RLM_REGEX_INREPLY;
139 else if (strcmp(inst->searchin_str, "proxy") == 0)
140 inst->searchin = RLM_REGEX_INPROXY;
141 else if (strcmp(inst->searchin_str, "proxy_reply") == 0)
142 inst->searchin = RLM_REGEX_INPROXYREPLY;
145 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
146 inst->searchin = RLM_REGEX_INPACKET;
149 dattr = dict_attrbyname(inst->attribute);
151 radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
156 /* Add the module instance name */
157 inst->name = cf_section_name2(conf); /* may be NULL */
162 static rlm_rcode_t do_attr_rewrite(void *instance, REQUEST *request)
164 rlm_attr_rewrite_t *inst = (rlm_attr_rewrite_t *) instance;
165 rlm_rcode_t rcode = 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];
183 if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE, 0, TAG_ANY)) != NULL){
184 if (inst->name == NULL || strcmp(inst->name,attr_vp->vp_strvalue))
185 return RLM_MODULE_NOOP;
189 /* new_attribute = yes */
190 if (!radius_xlat(replace_STR, sizeof(replace_STR), inst->replace, request, NULL, NULL)) {
191 DEBUG2("%s: xlat on replace string failed.", inst->name);
194 attr_vp = pairmake(inst->attribute,replace_STR,0);
195 if (attr_vp == NULL){
196 DEBUG2("%s: Could not add new attribute %s with value '%s'", inst->name,
197 inst->attribute,replace_STR);
200 switch(inst->searchin){
201 case RLM_REGEX_INPACKET:
202 pairadd(&request->packet->vps,attr_vp);
204 case RLM_REGEX_INCONFIG:
205 pairadd(&request->config_items,attr_vp);
207 case RLM_REGEX_INREPLY:
208 pairadd(&request->reply->vps,attr_vp);
211 case RLM_REGEX_INPROXY:
212 if (!request->proxy) {
213 pairbasicfree(attr_vp);
214 return RLM_MODULE_NOOP;
216 pairadd(&request->proxy->vps, attr_vp);
218 case RLM_REGEX_INPROXYREPLY:
219 if (!request->proxy_reply) {
220 pairbasicfree(attr_vp);
221 return RLM_MODULE_NOOP;
223 pairadd(&request->proxy_reply->vps, attr_vp);
227 radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", inst->name);
228 inst->searchin = RLM_REGEX_INPACKET;
229 pairadd(&request->packet->vps,attr_vp);
232 DEBUG2("%s: Added attribute %s with value '%s'", inst->name,inst->attribute,replace_STR);
233 rcode = RLM_MODULE_OK;
237 /* new_attribute = no */
238 switch (inst->searchin) {
239 case RLM_REGEX_INPACKET:
240 if (!inst->da->vendor && (inst->da->attr == PW_USER_NAME))
241 attr_vp = request->username;
242 else if (!inst->da->vendor && (inst->da->attr == PW_USER_PASSWORD))
243 attr_vp = request->password;
245 tmp = request->packet->vps;
247 case RLM_REGEX_INCONFIG:
248 tmp = request->config_items;
250 case RLM_REGEX_INREPLY:
251 tmp = request->reply->vps;
254 case RLM_REGEX_INPROXYREPLY:
255 if (!request->proxy_reply)
256 return RLM_MODULE_NOOP;
257 tmp = request->proxy_reply->vps;
259 case RLM_REGEX_INPROXY:
261 return RLM_MODULE_NOOP;
262 tmp = request->proxy->vps;
266 radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", inst->name);
267 inst->searchin = RLM_REGEX_INPACKET;
268 attr_vp = pairfind(request->packet->vps, inst->da->attr, inst->da->vendor, TAG_ANY);
273 attr_vp = pairfind(tmp, inst->da->attr, inst->da->vendor, TAG_ANY);
274 if (attr_vp == NULL) {
275 DEBUG2("%s: Could not find value pair for attribute %s", inst->name,inst->attribute);
278 if (attr_vp->length == 0){
279 DEBUG2("%s: Attribute %s string value NULL or of zero length", inst->name,inst->attribute);
282 cflags |= REG_EXTENDED;
286 if (!radius_xlat(search_STR, sizeof(search_STR), inst->search, request, NULL, NULL) && inst->search_len != 0) {
287 DEBUG2("%s: xlat on search string failed.", inst->name);
291 if ((err = regcomp(&preg,search_STR,cflags))) {
292 regerror(err, &preg, err_msg, MAX_STRING_LEN);
293 DEBUG2("%s: regcomp() returned error: %s", inst->name,err_msg);
297 if ((attr_vp->da->type == PW_TYPE_IPADDR) &&
298 (attr_vp->vp_strvalue[0] == '\0')) {
299 inet_ntop(AF_INET, &(attr_vp->vp_ipaddr),
300 attr_vp->vp_strvalue,
301 sizeof(attr_vp->vp_strvalue));
305 ptr2 = attr_vp->vp_strvalue;
308 for ( i = 0 ;i < (unsigned)inst->num_matches; i++) {
309 err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
310 if (err == REG_NOMATCH) {
312 DEBUG2("%s: Does not match: %s = %s", inst->name,
313 inst->attribute, attr_vp->vp_strvalue);
321 radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", inst->name,
322 inst->attribute, attr_vp->vp_strvalue);
325 if (pmatch[0].rm_so == -1)
327 len = pmatch[0].rm_so;
329 len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
332 if (counter >= MAX_STRING_LEN) {
334 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", inst->name,
335 inst->attribute, attr_vp->vp_strvalue);
339 memcpy(ptr, ptr2,len);
342 ptr2 += pmatch[0].rm_eo;
346 * We only run on the first match, sorry
348 for(j = 0; j <= REQUEST_MAX_REGEX; j++){
350 char buffer[sizeof(attr_vp->vp_strvalue)];
353 * Stolen from src/main/valuepair.c, paircompare()
357 * Delete old matches if the corresponding match does not
358 * exist in the current regex
360 if (pmatch[j].rm_so == -1){
361 p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
369 attr_vp->vp_strvalue + pmatch[j].rm_so,
370 pmatch[j].rm_eo - pmatch[j].rm_so);
371 buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
373 request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
378 if (inst->replace_len != 0 &&
379 radius_xlat(replace_STR, sizeof(replace_STR), inst->replace, request, NULL, NULL) == 0) {
380 DEBUG2("%s: xlat on replace string failed.", inst->name);
383 replace_len = (inst->replace_len != 0) ? strlen(replace_STR) : 0;
387 counter += replace_len;
388 if (counter >= MAX_STRING_LEN) {
390 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", inst->name,
391 inst->attribute, attr_vp->vp_strvalue);
395 memcpy(ptr, replace_STR, replace_len);
401 len = strlen(ptr2) + 1; /* We add the ending NULL */
403 if (counter >= MAX_STRING_LEN){
404 DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", inst->name,
405 inst->attribute, attr_vp->vp_strvalue);
408 memcpy(ptr, ptr2, len);
411 DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", inst->name,
412 inst->attribute, attr_vp->vp_strvalue, new_str);
413 if (!pairparsevalue(attr_vp, new_str)) {
414 DEBUG2("%s: Could not write value '%s' into attribute %s: %s", inst->name, new_str, inst->attribute, fr_strerror());
419 rcode = RLM_MODULE_OK;
431 static rlm_rcode_t attr_rewrite_accounting(void *instance, REQUEST *request)
433 return do_attr_rewrite(instance, request);
436 static rlm_rcode_t attr_rewrite_authorize(void *instance, REQUEST *request)
438 return do_attr_rewrite(instance, request);
441 static rlm_rcode_t attr_rewrite_authenticate(void *instance, REQUEST *request)
443 return do_attr_rewrite(instance, request);
446 static rlm_rcode_t attr_rewrite_preacct(void *instance, REQUEST *request)
448 return do_attr_rewrite(instance, request);
451 static rlm_rcode_t attr_rewrite_checksimul(void *instance, REQUEST *request)
453 return do_attr_rewrite(instance, request);
457 static rlm_rcode_t attr_rewrite_preproxy(void *instance, REQUEST *request)
459 return do_attr_rewrite(instance, request);
462 static rlm_rcode_t attr_rewrite_postproxy(void *instance, REQUEST *request)
464 return do_attr_rewrite(instance, request);
468 static rlm_rcode_t attr_rewrite_postauth(void *instance, REQUEST *request)
470 return do_attr_rewrite(instance, request);
474 * The module name should be the only globally exported symbol.
475 * That is, everything else should be 'static'.
477 * If the module needs to temporarily modify it's instantiation
478 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
479 * The server will then take care of ensuring that the module
480 * is single-threaded.
482 module_t rlm_attr_rewrite = {
485 RLM_TYPE_THREAD_UNSAFE, /* type */
486 attr_rewrite_instantiate, /* instantiation */
489 attr_rewrite_authenticate, /* authentication */
490 attr_rewrite_authorize, /* authorization */
491 attr_rewrite_preacct, /* preaccounting */
492 attr_rewrite_accounting, /* accounting */
493 attr_rewrite_checksimul, /* checksimul */
495 attr_rewrite_preproxy, /* pre-proxy */
496 attr_rewrite_postproxy, /* post-proxy */
500 attr_rewrite_postauth /* post-auth */