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