Delete trailing whitespace.
[freeradius.git] / src / modules / rlm_protocol_filter / rlm_protocol_filter.c
1 /*
2  * rlm_protocol_filter.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 2004  Cladju Consulting, Inc. <aland@cladju.com>
21  * Copyright 2006  The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29
30 /*
31  *      Define a structure for our module configuration.
32  *
33  */
34 typedef struct rlm_protocol_filter_t {
35         char            *filename;
36         char            *key;
37         CONF_SECTION    *cs;
38 } rlm_protocol_filter_t;
39
40 /*
41  *      A mapping of configuration file names to internal variables.
42  *
43  *      Note that the string is dynamically allocated, so it MUST
44  *      be freed.  When the configuration file parse re-reads the string,
45  *      it free's the old one, and strdup's the new one, placing the pointer
46  *      to the strdup'd string into 'config.string'.  This gets around
47  *      buffer over-flows.
48  */
49 static const CONF_PARSER module_config[] = {
50         { "filename",  PW_TYPE_FILENAME,
51           offsetof(rlm_protocol_filter_t,filename), NULL,
52           "${raddbdir}/protocol_filter.conf"},
53
54         { "key",  PW_TYPE_STRING_PTR,
55           offsetof(rlm_protocol_filter_t,key), NULL, "%{Realm:-DEFAULT}"},
56
57         { NULL, -1, 0, NULL, NULL }             /* end the list */
58 };
59
60 static int filter_detach(void *instance)
61 {
62         rlm_protocol_filter_t *inst = instance;
63
64         if (inst->cs) cf_section_free(&(inst->cs));
65
66         free(instance);
67         return 0;
68 }
69
70
71 /*
72  *      Do any per-module initialization that is separate to each
73  *      configured instance of the module.  e.g. set up connections
74  *      to external databases, read configuration files, set up
75  *      dictionary entries, etc.
76  *
77  *      If configuration information is given in the config section
78  *      that must be referenced in later calls, store a handle to it
79  *      in *instance otherwise put a null pointer there.
80  */
81 static int filter_instantiate(CONF_SECTION *conf, void **instance)
82 {
83         rlm_protocol_filter_t *inst;
84
85         /*
86          *      Set up a storage area for instance data
87          */
88         inst = rad_malloc(sizeof(*inst));
89         if (!inst) {
90                 return -1;
91         }
92         memset(inst, 0, sizeof(*inst));
93
94         /*
95          *      If the configuration parameters can't be parsed, then
96          *      fail.
97          */
98         if (cf_section_parse(conf, inst, module_config) < 0) {
99                 filter_detach(inst);
100                 return -1;
101         }
102
103         inst->cs = conf_read("rlm_protocol_filter", 0,
104                              inst->filename, NULL);
105         if (!inst->cs) {
106                 filter_detach(inst);
107                 return -1;
108         }
109
110         *instance = inst;
111
112         return 0;
113 }
114
115
116 /*
117  *      Return permission.
118  */
119 static int str2sense(const char *str)
120 {
121         if (strcasecmp(str, "permit") == 0) return 1;
122         if (strcasecmp(str, "deny") == 0) return 0;
123
124         return -1;
125 }
126
127 /*
128  *      Apply a subsection to a request.
129  *      Returns permit/deny/error.
130  */
131 static int apply_subsection(rlm_protocol_filter_t *inst, REQUEST *request,
132                             CONF_SECTION *cs, const char *name)
133 {
134         int sense;
135         CONF_PAIR *cp;
136         const char *value;
137         char keybuf[256];
138
139         DEBUG2("  rlm_protocol_filter: Found subsection %s", name);
140
141         cp = cf_pair_find(cs, "key");
142         if (!cp) {
143                 radlog(L_ERR, "rlm_protocol_filter: %s[%d]: No key defined in subsection %s",
144                        inst->filename, cf_section_lineno(cs), name);
145                 return RLM_MODULE_FAIL;
146         }
147
148         radius_xlat(keybuf, sizeof(keybuf),
149                     cf_pair_value(cp), request, NULL);
150         if (!*keybuf) {
151                 DEBUG2("  rlm_protocol_filter: %s[%d]: subsection %s, key is empty, doing nothing.",
152                        inst->filename, cf_section_lineno(cs), name);
153                 return RLM_MODULE_NOOP;
154         }
155
156         DEBUG2("  rlm_protocol_filter: %s[%d]: subsection %s, using key %s",
157                inst->filename, cf_section_lineno(cs), name, keybuf);
158
159         /*
160          *      And repeat some of the above code.
161          */
162         cp = cf_pair_find(cs, keybuf);
163         if (!cp) {
164                 CONF_SECTION *subcs;
165
166                 /*
167                  *      Maybe it has a subsection, too.
168                  */
169                 subcs = cf_section_sub_find(cs, keybuf);
170                 if (subcs) {
171                         return apply_subsection(inst, request, subcs, keybuf);
172                 } /* it was a subsection */
173
174
175
176                 DEBUG2("  rlm_protocol_filter: %s[%d]: subsection %s, rule not found, doing nothing.",
177                        inst->filename, cf_section_lineno(cs), name);
178                 return RLM_MODULE_NOOP;
179         }
180
181         value = cf_pair_value(cp);
182         sense = str2sense(value);
183         if (sense < 0) {
184                 radlog(L_ERR, "rlm_protocol_filter: %s[%d]: Unknwn directive %s",
185                        inst->filename, cf_pair_lineno(cp), value);
186                 return RLM_MODULE_FAIL;
187         }
188
189         if (!sense) return RLM_MODULE_REJECT;
190
191         return RLM_MODULE_OK;
192 }
193
194
195 /*
196  *      Authorize the user.
197  */
198 static int filter_authorize(void *instance, REQUEST *request)
199 {
200         int sense;
201         VALUE_PAIR *vp;
202         CONF_SECTION *cs;
203         CONF_PAIR *cp;
204         char keybuf[1024];
205         rlm_protocol_filter_t *inst = instance;
206
207         radius_xlat(keybuf, sizeof(keybuf), inst->key, request, NULL);
208         if (!*keybuf) {
209                 DEBUG2("  rlm_protocol_filter: key is empty");
210                 return RLM_MODULE_NOOP;
211         }
212         DEBUG2("  rlm_protocol_filter: Using key %s", keybuf);
213
214         cs = cf_section_sub_find(inst->cs, keybuf);
215         if (!cs) {
216                 DEBUG2("  rlm_protocol_filter: No such key in %s", inst->filename);
217                 return RLM_MODULE_NOTFOUND;
218         }
219
220         /*
221          *      Walk through the list of attributes, seeing if they're
222          *      permitted/denied.
223          */
224         for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
225                 const char *value;
226                 CONF_SECTION *subcs;
227
228                 cp = cf_pair_find(cs, vp->name);
229                 if (cp) {
230                         value = cf_pair_value(cp);
231
232                         sense = str2sense(value);
233                         if (sense < 0) {
234                                 radlog(L_ERR, "rlm_protocol_filter %s[%d]: Unknown directive %s",
235                                        inst->filename,
236                                        cf_pair_lineno(cp),
237                                        value);
238                                 return RLM_MODULE_FAIL;
239                         }
240
241                         if (!sense) return RLM_MODULE_REJECT;
242                         continue; /* was permitted */
243                 } /* else no pair was found */
244
245                 /*
246                  *      Maybe it has a subsection
247                  */
248                 subcs = cf_section_sub_find(cs, vp->name);
249                 if (subcs) {
250                         sense = apply_subsection(inst, request, subcs, vp->name);
251                         if ((sense == RLM_MODULE_OK) ||
252                             (sense == RLM_MODULE_NOOP)) {
253                                 continue;
254                         }
255
256                         return sense;
257                 } /* it was a subsection */
258
259                 /*
260                  *      Not found, must be "permit"
261                  */
262         }
263
264         return RLM_MODULE_OK;
265 }
266
267
268 /*
269  *      The module name should be the only globally exported symbol.
270  *      That is, everything else should be 'static'.
271  *
272  *      If the module needs to temporarily modify it's instantiation
273  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
274  *      The server will then take care of ensuring that the module
275  *      is single-threaded.
276  */
277 module_t rlm_protocol_filter = {
278         RLM_MODULE_INIT,
279         "protocol_filter",
280         RLM_TYPE_THREAD_SAFE,           /* type */
281         filter_instantiate,             /* instantiation */
282         filter_detach,                  /* detach */
283         {
284                 NULL,                   /* authentication */
285                 filter_authorize,       /* authorization */
286                 NULL,                   /* preaccounting */
287                 NULL,                   /* accounting */
288                 NULL,                   /* checksimul */
289                 NULL,                   /* pre-proxy */
290                 NULL,                   /* post-proxy */
291                 NULL                    /* post-auth */
292         },
293 };