Pull fix from branch_1_1
[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         free(instance);
85         return 0;
86 }
87
88 /*
89  *      Do any per-module initialization that is separate to each
90  *      configured instance of the module.  e.g. set up connections
91  *      to external databases, read configuration files, set up
92  *      dictionary entries, etc.
93  *
94  *      If configuration information is given in the config section
95  *      that must be referenced in later calls, store a handle to it
96  *      in *instance otherwise put a null pointer there.
97  */
98 static int checkval_instantiate(CONF_SECTION *conf, void **instance)
99 {
100         rlm_checkval_t *data;
101         DICT_ATTR *dattr;
102         ATTR_FLAGS flags;
103
104         static const LRAD_NAME_NUMBER names[] = {
105                 { "string", PW_TYPE_STRING },
106                 { "integer", PW_TYPE_INTEGER },
107                 { "ipaddr", PW_TYPE_IPADDR },
108                 { "date", PW_TYPE_DATE },
109                 { "abinary", PW_TYPE_OCTETS },
110                 { "octets", PW_TYPE_OCTETS },
111                 { "binary", PW_TYPE_OCTETS },
112                 { NULL, 0 }
113         };
114
115         /*
116          *      Set up a storage area for instance data
117          */
118         data = rad_malloc(sizeof(*data));
119         if (!data) {
120                 return -1;
121         }
122         memset(data, 0, sizeof(*data));
123
124         /*
125          *      If the configuration parameters can't be parsed, then
126          *      fail.
127          */
128         if (cf_section_parse(conf, data, module_config) < 0) {
129                 checkval_detach(data);
130                 return -1;
131         }
132
133         /*
134          * Check if data_type exists
135          */
136         if (!data->data_type || !strlen(data->data_type)){
137                 radlog(L_ERR, "rlm_checkval: Data type not defined");
138                 checkval_detach(data);
139                 return -1;
140         }
141         if (!data->item_name || !strlen(data->item_name)){
142                 radlog(L_ERR, "rlm_checkval: Item name not defined");
143                 checkval_detach(data);
144                 return -1;
145         }
146         if (!data->check_name || !strlen(data->check_name)){
147                 radlog(L_ERR, "rlm_checkval: Check item name not defined");
148                 checkval_detach(data);
149                 return -1;
150         }
151
152         /*
153          *      Discover the attribute number of the item name
154          */
155         dattr = dict_attrbyname(data->item_name);
156         if (!dattr) {
157                 radlog(L_ERR, "rlm_checkval: No such attribute %s",
158                        data->item_name);
159                 checkval_detach(data);
160                 return -1;
161         }
162         data->item_attr = dattr->attr;
163
164         /*
165          *      Add the check attribute name to the dictionary
166          *      if it does not already exists. dict_addattr() handles that
167          */
168
169         memset(&flags, 0, sizeof(flags));
170         dict_addattr(data->check_name, 0, PW_TYPE_STRING, -1,flags);
171         dattr = dict_attrbyname(data->check_name);
172         if (!dattr){
173                 radlog(L_ERR, "rlm_checkval: No such attribute %s",
174                        data->check_name);
175                 checkval_detach(data);
176                 return -1;
177         }
178         data->chk_attr = dattr->attr;
179         DEBUG2("rlm_checkval: Registered name %s for attribute %d",
180                 dattr->name,dattr->attr);
181
182         /*
183          *      Convert the string type to an integer type,
184          *      so we don't have to do string comparisons on each
185          *      packet.
186          */
187         data->dat_type = lrad_str2int(names, data->data_type, -1);
188         if (data->dat_type < 0) {
189                 radlog(L_ERR, "rlm_checkval: Data type %s in not known",data->data_type);
190                 checkval_detach(data);
191                 return -1;
192         }
193
194         *instance = data;
195
196         return 0;
197 }
198
199 static int do_checkval(void *instance, REQUEST *request)
200 {
201         rlm_checkval_t *data = (rlm_checkval_t *) instance;
202         int ret=RLM_MODULE_NOOP;
203         VALUE_PAIR *chk_vp, *item_vp;
204         VALUE_PAIR *tmp;
205         char found = 0;
206
207         /* quiet the compiler */
208         instance = instance;
209         request = request;
210
211
212         /*
213         *      Look for the check item
214         */
215
216         if (!(item_vp = pairfind(request->packet->vps, data->item_attr))){
217                 DEBUG2("rlm_checkval: Could not find item named %s in request", data->item_name);
218                 if (data->notfound_reject)
219                         ret = RLM_MODULE_REJECT;
220                 else
221                         ret = RLM_MODULE_NOTFOUND;
222         }
223         if (item_vp)
224                 DEBUG2("rlm_checkval: Item Name: %s, Value: %s",data->item_name, item_vp->vp_strvalue);
225         tmp = request->config_items;
226         do{
227                 if (!(chk_vp = pairfind(tmp, data->chk_attr))){
228                         if (!found){
229                                 DEBUG2("rlm_checkval: Could not find attribute named %s in check pairs",data->check_name);
230                                 ret = RLM_MODULE_NOTFOUND;
231                         }
232                         break;
233                 }
234                 if (!item_vp)
235                         break;
236                 DEBUG2("rlm_checkval: Value Name: %s, Value: %s",data->check_name, chk_vp->vp_strvalue);
237
238                 /*
239                 * Check if item != check
240                 */
241                 found = 1;
242                 if (data->dat_type == PW_TYPE_STRING ||
243                     data->dat_type == PW_TYPE_OCTETS) {
244                         if (item_vp->length != chk_vp->length)
245                                 ret = RLM_MODULE_REJECT;
246                         else{
247                                 if (!memcmp(item_vp->vp_strvalue,
248                                             chk_vp->vp_strvalue,
249                                             (size_t) chk_vp->length))
250                                         ret = RLM_MODULE_OK;
251                                 else
252                                         ret = RLM_MODULE_REJECT;
253                         }
254                 }
255                 else{   /* Integer or Date */
256
257                         if (item_vp->lvalue == chk_vp->lvalue)
258                                 ret = RLM_MODULE_OK;
259                         else
260                                 ret = RLM_MODULE_REJECT;
261                 }
262 #ifdef HAVE_REGEX_H
263                 if (ret == RLM_MODULE_REJECT &&
264                     chk_vp->operator == T_OP_REG_EQ) {
265                         regex_t reg;
266                         int err;
267                         char err_msg[MAX_STRING_LEN];
268
269                         DEBUG("rlm_checkval: Doing regex");
270                         err = regcomp(&reg, (char *)chk_vp->vp_strvalue, REG_EXTENDED|REG_NOSUB);
271                         if (err){
272                                 regerror(err, &reg,err_msg, MAX_STRING_LEN);
273                                 DEBUG("rlm_checkval: regcomp() returned error: %s", err_msg);
274                                 return RLM_MODULE_FAIL;
275                         }
276                         if (regexec(&reg, (char *)item_vp->vp_strvalue,0, NULL, 0) == 0)
277                                 ret = RLM_MODULE_OK;
278                         else
279                                 ret = RLM_MODULE_REJECT;
280                         regfree(&reg);
281                 }
282 #endif
283                 tmp = chk_vp->next;
284         } while (ret == RLM_MODULE_REJECT &&
285                  tmp != NULL);
286
287         if (ret == RLM_MODULE_REJECT) {
288                 if (!item_vp && data->notfound_reject){
289                         char module_fmsg[MAX_STRING_LEN];
290                         VALUE_PAIR *module_fmsg_vp;
291
292                         snprintf(module_fmsg,sizeof(module_fmsg),
293                                 "rlm_checkval: Could not find item named %s in request", data->item_name);
294                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
295                         pairadd(&request->packet->vps, module_fmsg_vp);
296                 }
297                 else{
298                         char module_fmsg[MAX_STRING_LEN];
299                         VALUE_PAIR *module_fmsg_vp;
300
301                         snprintf(module_fmsg,sizeof(module_fmsg),
302                                 "rlm_checkval: This %s is not allowed for the user", data->item_name);
303                         module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
304                         pairadd(&request->packet->vps, module_fmsg_vp);
305                 }
306         }
307
308
309         return ret;
310 }
311
312 /*
313  */
314 static int checkval_authorize(void *instance, REQUEST *request)
315 {
316         return do_checkval(instance,request);
317 }
318
319 static int checkval_accounting(void *instance, REQUEST *request)
320 {
321         return do_checkval(instance,request);
322 }
323
324 /*
325  *      The module name should be the only globally exported symbol.
326  *      That is, everything else should be 'static'.
327  *
328  *      If the module needs to temporarily modify it's instantiation
329  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
330  *      The server will then take care of ensuring that the module
331  *      is single-threaded.
332  */
333 module_t rlm_checkval = {
334          RLM_MODULE_INIT,
335         "checkval",
336         0,              /* type */
337         checkval_instantiate,           /* instantiation */
338         checkval_detach,                /* detach */
339         {
340                 NULL,                   /* authentication */
341                 checkval_authorize,     /* authorization */
342                 NULL,                   /* preaccounting */
343                 checkval_accounting,    /* accounting */
344                 NULL,                   /* checksimul */
345                 NULL,                   /* pre-proxy */
346                 NULL,                   /* post-proxy */
347                 NULL                    /* post-auth */
348         },
349 };