Do an xlat on the search and replace strings
[freeradius.git] / src / modules / rlm_attr_rewrite / rlm_attr_rewrite.c
1 /*
2  * rlm_attr_rewrite.c
3  *
4  * Version:  $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  * Copyright 2001  Kostas Kalevras <kkalev@noc.ntua.gr>
22  */
23
24 #include "config.h"
25 #include "autoconf.h"
26 #include "libradius.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include "config.h"
32 #if HAVE_REGEX_H
33 #       include <regex.h>
34 #endif
35
36 #include "radiusd.h"
37 #include "modules.h"
38 #include "conffile.h"
39
40 #define RLM_REGEX_INPACKET 0
41 #define RLM_REGEX_INCONFIG 1
42 #define RLM_REGEX_INREPLY  2
43
44 static const char rcsid[] = "$Id$";
45
46 typedef struct rlm_attr_rewrite_t {
47         char *attribute;        /* The attribute to search for */
48         int  attr_num;          /* The attribute number */
49         char *search;           /* The pattern to search for */
50         char *searchin_str;     /* The VALUE_PAIR list to search in. Can be either packet,reply or config */
51         char searchin;          /* The same as above just coded as a number for speed */
52         char *replace;          /* The replacement */
53         int  nocase;            /* Ignore case */
54         int  num_matches;       /* Maximum number of matches */
55 } rlm_attr_rewrite_t;
56
57
58 static CONF_PARSER module_config[] = {
59   { "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
60   { "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
61   { "searchin",  PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
62   { "replacewith", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
63   { "ignore_case", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,nocase), NULL, "1" },
64   { "max_matches", PW_TYPE_INTEGER, offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
65   { NULL, -1, 0, NULL, NULL }
66 };
67
68
69 static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
70 {
71         rlm_attr_rewrite_t *data;
72         DICT_ATTR *dattr;
73         
74         /*
75          *      Set up a storage area for instance data
76          */
77         data = rad_malloc(sizeof(*data));
78
79         /*
80          *      If the configuration parameters can't be parsed, then
81          *      fail.
82          */
83         if (cf_section_parse(conf, data, module_config) < 0) {
84                 free(data);
85                 return -1;
86         }
87
88         /*
89          *      Discover the attribute number of the key. 
90          */
91         if (data->attribute == NULL) {
92                 radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
93                 return -1;
94         }
95         if (data->search == NULL || data->replace == NULL) {
96                 radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
97                 return -1;
98         }
99
100         if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) {
101                 radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
102                 return -1;
103         }
104         if (data->searchin_str == NULL) {
105                 radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
106                 data->searchin = RLM_REGEX_INPACKET;
107         }
108         else{
109                 if (strcmp(data->searchin_str, "packet") == 0)
110                         data->searchin = RLM_REGEX_INPACKET;
111                 else if (strcmp(data->searchin_str, "config") == 0)
112                         data->searchin = RLM_REGEX_INCONFIG;
113                 else if (strcmp(data->searchin_str, "reply") == 0)
114                         data->searchin = RLM_REGEX_INREPLY;
115                 else {
116                         radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
117                         data->searchin = RLM_REGEX_INPACKET;
118                 }
119                 free((char *)data->searchin_str);
120         }
121         dattr = dict_attrbyname(data->attribute);
122         if (dattr == NULL) {
123                 radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
124                                 data->attribute);
125                 return -1;
126         }
127         data->attr_num = dattr->attr;
128         
129         *instance = data;
130         
131         return 0;
132 }
133
134 static int do_attr_rewrite(void *instance, REQUEST *request)
135 {
136         rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
137         int ret = RLM_MODULE_NOOP;
138         VALUE_PAIR *attr_vp = NULL;
139         regex_t preg;
140         regmatch_t pmatch;
141         int cflags = 0;
142         int err = 0;
143         unsigned int len = 0;
144         char err_msg[MAX_STRING_LEN];
145         unsigned int i = 0;
146         unsigned int counter = 0;
147         char new_str[MAX_STRING_LEN];
148         char *ptr, *ptr2;
149         char search_STR[MAX_STRING_LEN];
150         char replace_STR[MAX_STRING_LEN];
151         int replace_len = 0;
152
153         switch (data->searchin) {
154                 case RLM_REGEX_INPACKET:
155                         if (data->attr_num == PW_USER_NAME)
156                                 attr_vp = request->username;
157                         else if (data->attr_num == PW_PASSWORD)
158                                 attr_vp = request->password;
159                         else
160                                 attr_vp = pairfind(request->packet->vps, data->attr_num);
161                         break;
162                 case RLM_REGEX_INCONFIG:
163                         attr_vp = pairfind(request->config_items, data->attr_num);
164                         break;
165                 case RLM_REGEX_INREPLY:
166                         attr_vp = pairfind(request->reply->vps, data->attr_num);
167                         break;
168                 default:
169                         radlog(L_ERR, "rlm_attr_rewrite: Illegal value for searchin. Changing to packet.");
170                         data->searchin = RLM_REGEX_INPACKET;
171                         attr_vp = pairfind(request->packet->vps, data->attr_num);
172                         break;
173         }
174         if (attr_vp == NULL) {
175                 DEBUG2("rlm_attr_rewrite: Could not find value pair for attribute %s",data->attribute);
176                 return ret;
177         }
178         if (attr_vp->strvalue == NULL || attr_vp->length == 0){
179                 DEBUG2("rlm_attr_rewrite: Attribute %s string value NULL or of zero length",data->attribute);
180                 return ret;
181         }
182         cflags |= REG_EXTENDED;
183         if (data->nocase)
184                 cflags |= REG_ICASE;
185
186         if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL)) {
187                 DEBUG2("rlm_attr_rewrite: xlat on search string failed.");
188                 return ret;
189         }
190         if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
191                 DEBUG2("rlm_attr_rewrite: xlat on replace string failed.");
192                 return ret;
193         }
194         replace_len = strlen(replace_STR);
195
196         if ((err = regcomp(&preg,search_STR,cflags))) {
197                 regerror(err, &preg, err_msg, MAX_STRING_LEN);
198                 DEBUG2("rlm_attr_rewrite: regcomp() returned error: %s",err_msg);
199                 return ret;
200         }
201         ptr = new_str;
202         ptr2 = attr_vp->strvalue;
203         counter = 0;
204
205         for ( /**/ ;i < data->num_matches; i++) {
206                 err = regexec(&preg, ptr2, 1, &pmatch, 0);
207                 if (err == REG_NOMATCH) {
208                         if (i == 0) {
209                                 DEBUG2("rlm_attr_rewrite: No match found for attribute %s with value %s",
210                                                 data->attribute, attr_vp->strvalue);
211                                 regfree(&preg);
212                                 return RLM_MODULE_OK;
213                         } else
214                                 break;
215                 }
216                 if (err != 0) {
217                         regfree(&preg);
218                         radlog(L_ERR, "rlm_attr_rewrite: match failure for attribute %s with value %s",
219                                         data->attribute, attr_vp->strvalue);
220                         return ret;
221                 }
222                 if (pmatch.rm_so == -1)
223                         break;
224                 len = pmatch.rm_so;
225                 counter += len;
226                 if (counter >= MAX_STRING_LEN) {
227                         regfree(&preg);
228                         DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value %s",
229                                         data->attribute, attr_vp->strvalue);    
230                         return ret;
231                 }
232
233                 strncpy(ptr, ptr2,len);
234                 ptr += len;
235                 ptr2 += pmatch.rm_eo;
236
237                 counter += replace_len;
238                 if (counter >= MAX_STRING_LEN) {
239                         regfree(&preg);
240                         DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value %s",
241                                         data->attribute, attr_vp->strvalue);    
242                         return ret;
243                 }
244                 strncpy(ptr, replace_STR, replace_len);
245                 ptr += replace_len;     
246         }
247         regfree(&preg);
248         len = strlen(ptr2) + 1;         /* We add the ending NULL */
249         counter += len;
250         if (counter >= MAX_STRING_LEN){
251                 DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value %s",
252                                 data->attribute, attr_vp->strvalue);    
253                 return ret;
254         }
255         strncpy(ptr, ptr2, len);
256
257         DEBUG2("rlm_attr_rewrite: Changed value for attribute %s from %s to %s",
258                         data->attribute, attr_vp->strvalue, new_str);
259         attr_vp->length = strlen(new_str);
260         strncpy(attr_vp->strvalue, new_str, (attr_vp->length + 1));
261
262         ret = RLM_MODULE_OK;
263
264         return ret;
265 }
266
267
268 static int attr_rewrite_accounting(void *instance, REQUEST *request)
269 {
270         return do_attr_rewrite(instance, request);
271 }
272
273 static int attr_rewrite_authorize(void *instance, REQUEST *request)
274 {
275         return do_attr_rewrite(instance, request);
276 }
277 static int attr_rewrite_authenticate(void *instance, REQUEST *request)
278 {
279         return do_attr_rewrite(instance, request);
280 }
281 static int attr_rewrite_preacct(void *instance, REQUEST *request)
282 {
283         return do_attr_rewrite(instance, request);
284 }
285 static int attr_rewrite_ismul(void *instance, REQUEST *request)
286 {
287         return do_attr_rewrite(instance, request);
288 }
289
290 static int attr_rewrite_detach(void *instance)
291 {
292         rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
293
294         free(data->attribute);
295         free(data->search);
296         free(data->replace);
297
298         free(instance);
299         return 0;
300 }
301
302 /*
303  *      The module name should be the only globally exported symbol.
304  *      That is, everything else should be 'static'.
305  *
306  *      If the module needs to temporarily modify it's instantiation
307  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
308  *      The server will then take care of ensuring that the module
309  *      is single-threaded.
310  */
311 module_t rlm_attr_rewrite = {
312         "attr_rewrite", 
313         RLM_TYPE_THREAD_UNSAFE,         /* type */
314         NULL,                           /* initialization */
315         attr_rewrite_instantiate,               /* instantiation */
316         {
317                 attr_rewrite_authenticate,      /* authentication */
318                 attr_rewrite_authorize,         /* authorization */
319                 attr_rewrite_preacct,           /* preaccounting */
320                 attr_rewrite_accounting,        /* accounting */
321                 attr_rewrite_ismul              /* checksimul */
322         },
323         attr_rewrite_detach,                    /* detach */
324         NULL,                           /* destroy */
325 };