c430382881271ebb3af9ac7605b7c74f82ac1999
[freeradius.git] / src / modules / rlm_checkval / rlm_checkval.c
1 /*
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.
5  *
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.
10  *
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
14  */
15
16 /**
17  * $Id$
18  * @file rlm_checkval.c
19  * @brief Enables simple value checking.
20  *
21  * @copyright 2003,2006  The FreeRADIUS server project
22  * @copyright 2003  Kostas Kalevras <kkalev@noc.ntua.gr>
23  */
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #ifdef HAVE_REGEX_H
31 #       include <regex.h>
32 #endif
33 #ifndef REG_EXTENDED
34 #define REG_EXTENDED (0)
35 #endif
36
37 #ifndef REG_NOSUB
38 #define REG_NOSUB (0)
39 #endif
40 /*
41  *      Define a structure for our module configuration.
42  *
43  *      These variables do not need to be in a structure, but it's
44  *      a lot cleaner to do so, and a pointer to the structure can
45  *      be used as the instance handle.
46  */
47 typedef struct rlm_checkval_t {
48         char            *item_name;     //!< The attribute inside
49                                         //!< Access-Request i.e.
50                                         //!< Calling-Station-Id.
51         char            *check_name;    //!< The attribute to check it with ie
52                                         //!< Allowed-Calling-Station-Id.
53         char            *data_type;     //!< String, integer, ipaddr, date,
54                                         //!< abinary,octets.
55         PW_TYPE         type;           //!< Resolved data type.
56
57         const DICT_ATTR *item;
58         const DICT_ATTR *check;
59
60         int             notfound_reject;        //!< If we don't find the
61                                                 //!< item_name in the request
62                                                 //!< send back a reject.
63 } rlm_checkval_t;
64
65 /*
66  *      A mapping of configuration file names to internal variables.
67  *
68  *      Note that the string is dynamically allocated, so it MUST
69  *      be freed.  When the configuration file parse re-reads the string,
70  *      it free's the old one, and strdup's the new one, placing the pointer
71  *      to the strdup'd string into 'config.string'.  This gets around
72  *      buffer over-flows.
73  */
74 static const CONF_PARSER module_config[] = {
75         { "item-name", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
76           offsetof(rlm_checkval_t,item_name), NULL,  NULL},
77         { "check-name", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
78           offsetof(rlm_checkval_t,check_name), NULL,  NULL},
79         { "data-type", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
80           offsetof(rlm_checkval_t,data_type),NULL, "integer"},
81         { "notfound-reject", PW_TYPE_BOOLEAN,
82           offsetof(rlm_checkval_t,notfound_reject),NULL, "no"},
83         { NULL, -1, 0, NULL, NULL }             /* end the list */
84 };
85
86 /*
87  *      Do any per-module initialization that is separate to each
88  *      configured instance of the module.  e.g. set up connections
89  *      to external databases, read configuration files, set up
90  *      dictionary entries, etc.
91  *
92  *      If configuration information is given in the config section
93  *      that must be referenced in later calls, store a handle to it
94  *      in *instance otherwise put a null pointer there.
95  */
96 static int mod_instantiate(CONF_SECTION *conf, void *instance)
97 {
98         rlm_checkval_t *inst = instance;
99         const DICT_ATTR *da;
100         ATTR_FLAGS flags;
101
102         rad_assert(inst->data_type && *inst->data_type);        
103         rad_assert(inst->item_name && *inst->item_name);
104         rad_assert(inst->check_name && *inst->check_name);
105
106         /*
107          *      Convert the string type to an integer type,
108          *      so we don't have to do string comparisons on each
109          *      packet.
110          */
111         inst->type = fr_str2int(dict_attr_types, inst->data_type, 0);
112         if (!inst->type) {
113                 cf_log_err_cs(conf, "Invalid data-type '%s'",
114                               inst->data_type);
115                 return -1;
116         }
117
118         /*
119          *      Discover the attribute number of the item name
120          */
121         inst->item = dict_attrbyname(inst->item_name);
122         rad_assert(inst->item != NULL);
123
124         /*
125          *      Add the check attribute name to the dictionary
126          *      if it does not already exists. dict_addattr() handles that
127          */
128         memset(&flags, 0, sizeof(flags));
129
130         dict_addattr(inst->check_name, -1, 0, PW_TYPE_STRING, flags);
131         da = dict_attrbyname(inst->check_name);
132         if (!da){
133                 ERROR("rlm_checkval: No such attribute %s",
134                        inst->check_name);
135                 return -1;
136         }
137         inst->check = da;
138         DEBUG2("rlm_checkval: Registered name %s for attribute %d",
139                 da->name,da->attr);
140
141         return 0;
142 }
143
144 static rlm_rcode_t do_checkval(void *instance, REQUEST *request)
145 {
146         rlm_checkval_t *inst = instance;
147         rlm_rcode_t rcode = RLM_MODULE_NOOP;
148         VALUE_PAIR *check, *item;
149         VALUE_PAIR *tmp;
150         char found = 0;
151
152         /*
153          *      Look for the check item
154          */
155         if (!(item = pairfind(request->packet->vps, inst->item->attr, inst->item->vendor, TAG_ANY))){
156                 DEBUG2("rlm_checkval: Could not find item named %s in request", inst->item_name);
157                 if (inst->notfound_reject)
158                         rcode = RLM_MODULE_REJECT;
159                 else
160                         rcode = RLM_MODULE_NOTFOUND;
161         }
162         if (item)
163                 DEBUG2("rlm_checkval: Item Name: %s, Value: %s",inst->item_name, item->vp_strvalue);
164         tmp = request->config_items;
165         do{
166                 if (!(check = pairfind(tmp, inst->check->attr, inst->check->vendor, TAG_ANY))){
167                         if (!found){
168                                 DEBUG2("rlm_checkval: Could not find attribute named %s in check pairs",inst->check_name);
169                                 rcode = RLM_MODULE_NOTFOUND;
170                         }
171                         break;
172                 }
173                 if (!item)
174                         break;
175                 DEBUG2("rlm_checkval: Value Name: %s, Value: %s",inst->check_name, check->vp_strvalue);
176
177                 /*
178                 * Check if item != check
179                 *
180                 *       FIXME:  !!! Call normal API functions!
181                 */
182                 found = 1;
183                 if (inst->type == PW_TYPE_STRING ||
184                     inst->type == PW_TYPE_OCTETS) {
185                         if (item->length != check->length)
186                                 rcode = RLM_MODULE_REJECT;
187                         else{
188                                 if (!memcmp(item->vp_strvalue,
189                                             check->vp_strvalue,
190                                             (size_t) check->length))
191                                         rcode = RLM_MODULE_OK;
192                                 else
193                                         rcode = RLM_MODULE_REJECT;
194                         }
195                 } else if (inst->type == PW_TYPE_DATE) {
196                         if (item->vp_date == check->vp_date)
197                                 rcode = RLM_MODULE_OK;
198                         else
199                                 rcode = RLM_MODULE_REJECT;
200                 } else if (inst->type == PW_TYPE_INTEGER) {
201                         if (item->vp_integer == check->vp_integer)
202                                 rcode = RLM_MODULE_OK;
203                         else
204                                 rcode = RLM_MODULE_REJECT;
205                 }
206 #ifdef HAVE_REGEX_H
207                 if (rcode == RLM_MODULE_REJECT &&
208                     check->op == T_OP_REG_EQ) {
209                         regex_t reg;
210                         int err;
211                         char err_msg[MAX_STRING_LEN];
212
213                         DEBUG("rlm_checkval: Doing regex");
214                         err = regcomp(&reg, (char *)check->vp_strvalue, REG_EXTENDED|REG_NOSUB);
215                         if (err){
216                                 regerror(err, &reg,err_msg, MAX_STRING_LEN);
217                                 DEBUG("rlm_checkval: regcomp() returned error: %s", err_msg);
218                                 return RLM_MODULE_FAIL;
219                         }
220                         if (regexec(&reg, (char *)item->vp_strvalue,0, NULL, 0) == 0)
221                                 rcode = RLM_MODULE_OK;
222                         else
223                                 rcode = RLM_MODULE_REJECT;
224                         regfree(&reg);
225                 }
226 #endif
227                 tmp = check->next;
228         } while (rcode == RLM_MODULE_REJECT &&
229                  tmp != NULL);
230
231         if (rcode == RLM_MODULE_REJECT) {
232                 if (!item && inst->notfound_reject){
233                         RDEBUGE("Could not find item named %s in request", inst->item_name);
234                 } else {
235                         RDEBUGE("This %s is not allowed for the user", inst->item_name);
236                 }
237         }
238
239
240         return rcode;
241 }
242
243 /*
244  */
245 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
246 {
247         return do_checkval(instance,request);
248 }
249
250 static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
251 {
252         return do_checkval(instance,request);
253 }
254
255 /*
256  *      The module name should be the only globally exported symbol.
257  *      That is, everything else should be 'static'.
258  *
259  *      If the module needs to temporarily modify it's instantiation
260  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
261  *      The server will then take care of ensuring that the module
262  *      is single-threaded.
263  */
264 module_t rlm_checkval = {
265         RLM_MODULE_INIT,
266         "checkval",
267         0,                              /* type */
268         sizeof(rlm_checkval_t),
269         module_config,
270         mod_instantiate,                /* instantiation */
271         NULL,                           /* detach */
272         {
273                 NULL,                   /* authentication */
274                 mod_authorize,  /* authorization */
275                 NULL,                   /* preaccounting */
276                 mod_accounting, /* accounting */
277                 NULL,                   /* checksimul */
278                 NULL,                   /* pre-proxy */
279                 NULL,                   /* post-proxy */
280                 NULL                    /* post-auth */
281         },
282 };