Pull fix from branch_1_1, so proxied EAP replies work
[freeradius.git] / src / modules / rlm_checkval / rlm_checkval.c
1 /*
2  * rlm_checkval.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2003,2006  The FreeRADIUS server project
21  * Copyright 2003  Kostas Kalevras <kkalev@noc.ntua.gr>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/autoconf.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/modules.h>
35 #ifdef HAVE_REGEX_H
36 #       include <regex.h>
37 #endif
38 #ifndef REG_EXTENDED
39 #define REG_EXTENDED (0)
40 #endif
41
42 #ifndef REG_NOSUB
43 #define REG_NOSUB (0)
44 #endif
45 /*
46  *      Define a structure for our module configuration.
47  *
48  *      These variables do not need to be in a structure, but it's
49  *      a lot cleaner to do so, and a pointer to the structure can
50  *      be used as the instance handle.
51  */
52 typedef struct rlm_checkval_t {
53         char    *item_name;     /* The attribute inside Access-Request ie Calling-Station-Id */
54         char    *check_name;    /* The attribute to check it with ie Allowed-Calling-Station-Id */
55         char    *data_type;     /* string,integer,ipaddr,date,abinary,octets */
56         int     dat_type;
57         int     item_attr;
58         int     chk_attr;
59         int     notfound_reject;        /* If we don't find the item_name in the request send back a reject */
60 } rlm_checkval_t;
61
62 /*
63  *      A mapping of configuration file names to internal variables.
64  *
65  *      Note that the string is dynamically allocated, so it MUST
66  *      be freed.  When the configuration file parse re-reads the string,
67  *      it free's the old one, and strdup's the new one, placing the pointer
68  *      to the strdup'd string into 'config.string'.  This gets around
69  *      buffer over-flows.
70  */
71 static const CONF_PARSER module_config[] = {
72   { "item-name",  PW_TYPE_STRING_PTR, offsetof(rlm_checkval_t,item_name), NULL,  NULL},
73   { "check-name",  PW_TYPE_STRING_PTR, offsetof(rlm_checkval_t,check_name), NULL,  NULL},
74   { "data-type",    PW_TYPE_STRING_PTR, offsetof(rlm_checkval_t,data_type),NULL, "integer"},
75   { "notfound-reject", PW_TYPE_BOOLEAN, offsetof(rlm_checkval_t,notfound_reject),NULL, "no"},
76   { NULL, -1, 0, NULL, NULL }           /* end the list */
77 };
78
79
80 static int checkval_detach(void *instance)
81 {
82         rlm_checkval_t *data = (rlm_checkval_t *) instance;
83
84         if (data->item_name)
85                 free((char *)data->item_name);
86         if (data->check_name)
87                 free((char *)data->check_name);
88         if (data->data_type)
89                 free((char *)data->data_type);
90
91         free(instance);
92         return 0;
93 }
94
95 /*
96  *      Do any per-module initialization that is separate to each
97  *      configured instance of the module.  e.g. set up connections
98  *      to external databases, read configuration files, set up
99  *      dictionary entries, etc.
100  *
101  *      If configuration information is given in the config section
102  *      that must be referenced in later calls, store a handle to it
103  *      in *instance otherwise put a null pointer there.
104  */
105 static int checkval_instantiate(CONF_SECTION *conf, void **instance)
106 {
107         rlm_checkval_t *data;
108         DICT_ATTR *dattr;
109         ATTR_FLAGS flags;
110
111         static const LRAD_NAME_NUMBER names[] = {
112                 { "string", PW_TYPE_STRING },
113                 { "integer", PW_TYPE_INTEGER },
114                 { "ipaddr", PW_TYPE_IPADDR },
115                 { "date", PW_TYPE_DATE },
116                 { "abinary", PW_TYPE_OCTETS },
117                 { "octets", PW_TYPE_OCTETS },
118                 { "binary", PW_TYPE_OCTETS },
119                 { NULL, 0 }
120         };
121
122         /*
123          *      Set up a storage area for instance data
124          */
125         data = rad_malloc(sizeof(*data));
126         if (!data) {
127                 return -1;
128         }
129         memset(data, 0, sizeof(*data));
130
131         /*
132          *      If the configuration parameters can't be parsed, then
133          *      fail.
134          */
135         if (cf_section_parse(conf, data, module_config) < 0) {
136                 checkval_detach(data);
137                 return -1;
138         }
139
140         /*
141          * Check if data_type exists
142          */
143         if (!data->data_type || !strlen(data->data_type)){
144                 radlog(L_ERR, "rlm_checkval: Data type not defined");
145                 checkval_detach(data);
146                 return -1;
147         }
148         if (!data->item_name || !strlen(data->item_name)){
149                 radlog(L_ERR, "rlm_checkval: Item name not defined");
150                 checkval_detach(data);
151                 return -1;
152         }
153         if (!data->check_name || !strlen(data->check_name)){
154                 radlog(L_ERR, "rlm_checkval: Check item name not defined");
155                 checkval_detach(data);
156                 return -1;
157         }
158
159         /*
160          *      Discover the attribute number of the item name
161          */
162         dattr = dict_attrbyname(data->item_name);
163         if (!dattr) {
164                 radlog(L_ERR, "rlm_checkval: No such attribute %s",
165                        data->item_name);
166                 checkval_detach(data);
167                 return -1;
168         }
169         data->item_attr = dattr->attr;
170
171         /*
172          *      Add the check attribute name to the dictionary
173          *      if it does not already exists. dict_addattr() handles that
174          */
175
176         memset(&flags, 0, sizeof(flags));
177         dict_addattr(data->check_name, 0, PW_TYPE_STRING, -1,flags);
178         dattr = dict_attrbyname(data->check_name);
179         if (!dattr){
180                 radlog(L_ERR, "rlm_checkval: No such attribute %s",
181                        data->check_name);
182                 checkval_detach(data);
183                 return -1;
184         }
185         data->chk_attr = dattr->attr;
186         DEBUG2("rlm_checkval: Registered name %s for attribute %d",
187                 dattr->name,dattr->attr);
188
189         /*
190          *      Convert the string type to an integer type,
191          *      so we don't have to do string comparisons on each
192          *      packet.
193          */
194         data->dat_type = lrad_str2int(names, data->data_type, -1);
195         if (data->dat_type < 0) {
196                 radlog(L_ERR, "rlm_checkval: Data type %s in not known",data->data_type);
197                 checkval_detach(data);
198                 return -1;
199         }
200
201         *instance = data;
202
203         return 0;
204 }
205
206 static int do_checkval(void *instance, REQUEST *request)
207 {
208         rlm_checkval_t *data = (rlm_checkval_t *) instance;
209         int ret=RLM_MODULE_NOOP;
210         VALUE_PAIR *chk_vp, *item_vp;
211         VALUE_PAIR *tmp;
212         char found = 0;
213
214         /* quiet the compiler */
215         instance = instance;
216         request = request;
217
218
219         /*
220         *      Look for the check item
221         */
222
223         if (!(item_vp = pairfind(request->packet->vps, data->item_attr))){
224                 DEBUG2("rlm_checkval: Could not find item named %s in request", data->item_name);
225                 if (data->notfound_reject)
226                         ret = RLM_MODULE_REJECT;
227                 else
228                         ret = RLM_MODULE_NOTFOUND;
229         }
230         if (item_vp)
231                 DEBUG2("rlm_checkval: Item Name: %s, Value: %s",data->item_name, item_vp->vp_strvalue);
232         tmp = request->config_items;
233         do{
234                 if (!(chk_vp = pairfind(tmp, data->chk_attr))){
235                         if (!found){
236                                 DEBUG2("rlm_checkval: Could not find attribute named %s in check pairs",data->check_name);
237                                 ret = RLM_MODULE_NOTFOUND;
238                         }
239                         break;
240                 }
241                 if (!item_vp)
242                         break;
243                 DEBUG2("rlm_checkval: Value Name: %s, Value: %s",data->check_name, chk_vp->vp_strvalue);
244
245                 /*
246                 * Check if item != check
247                 */
248                 found = 1;
249                 if (data->dat_type == PW_TYPE_STRING ||
250                     data->dat_type == PW_TYPE_OCTETS) {
251                         if (item_vp->length != chk_vp->length)
252                                 ret = RLM_MODULE_REJECT;
253                         else{
254                                 if (!memcmp(item_vp->vp_strvalue,
255                                             chk_vp->vp_strvalue,
256                                             (size_t) chk_vp->length))
257                                         ret = RLM_MODULE_OK;
258                                 else
259                                         ret = RLM_MODULE_REJECT;
260                         }
261                 }
262                 else{   /* Integer or Date */
263
264                         if (item_vp->lvalue == chk_vp->lvalue)
265                                 ret = RLM_MODULE_OK;
266                         else
267                                 ret = RLM_MODULE_REJECT;
268                 }
269 #ifdef HAVE_REGEX_H
270                 if (ret == RLM_MODULE_REJECT &&
271                     chk_vp->operator == T_OP_REG_EQ) {
272                         regex_t reg;
273                         int err;
274                         char err_msg[MAX_STRING_LEN];
275
276                         DEBUG("rlm_checkval: Doing regex");
277                         err = regcomp(&reg, (char *)chk_vp->vp_strvalue, REG_EXTENDED|REG_NOSUB);
278                         if (err){
279                                 regerror(err, &reg,err_msg, MAX_STRING_LEN);
280                                 DEBUG("rlm_checkval: regcomp() returned error: %s", err_msg);
281                                 return RLM_MODULE_FAIL;
282                         }
283                         if (regexec(&reg, (char *)item_vp->vp_strvalue,0, NULL, 0) == 0)
284                                 ret = RLM_MODULE_OK;
285                         else
286                                 ret = RLM_MODULE_REJECT;
287                         regfree(&reg);
288                 }
289 #endif
290                 tmp = chk_vp->next;
291         } while (ret == RLM_MODULE_REJECT &&
292                  tmp != NULL);
293
294         if (ret == RLM_MODULE_REJECT) {
295                 if (!item_vp && data->notfound_reject){
296                         char module_fmsg[MAX_STRING_LEN];
297                         VALUE_PAIR *module_fmsg_vp;
298
299                         snprintf(module_fmsg,sizeof(module_fmsg),
300                                 "rlm_checkval: Could not find item named %s in request", data->item_name);
301                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
302                         pairadd(&request->packet->vps, module_fmsg_vp);
303                 }
304                 else{
305                         char module_fmsg[MAX_STRING_LEN];
306                         VALUE_PAIR *module_fmsg_vp;
307
308                         snprintf(module_fmsg,sizeof(module_fmsg),
309                                 "rlm_checkval: This %s is not allowed for the user", data->item_name);
310                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
311                         pairadd(&request->packet->vps, module_fmsg_vp);
312                 }
313         }
314
315
316         return ret;
317 }
318
319 /*
320  */
321 static int checkval_authorize(void *instance, REQUEST *request)
322 {
323         return do_checkval(instance,request);
324 }
325
326 static int checkval_accounting(void *instance, REQUEST *request)
327 {
328         return do_checkval(instance,request);
329 }
330
331 /*
332  *      The module name should be the only globally exported symbol.
333  *      That is, everything else should be 'static'.
334  *
335  *      If the module needs to temporarily modify it's instantiation
336  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
337  *      The server will then take care of ensuring that the module
338  *      is single-threaded.
339  */
340 module_t rlm_checkval = {
341          RLM_MODULE_INIT,
342         "checkval",
343         0,              /* type */
344         checkval_instantiate,           /* instantiation */
345         checkval_detach,                /* detach */
346         {
347                 NULL,                   /* authentication */
348                 checkval_authorize,     /* authorization */
349                 NULL,                   /* preaccounting */
350                 checkval_accounting,    /* accounting */
351                 NULL,                   /* checksimul */
352                 NULL,                   /* pre-proxy */
353                 NULL,                   /* post-proxy */
354                 NULL                    /* post-auth */
355         },
356 };