Require that the modules call talloc for their instance handle.
[freeradius.git] / src / modules / rlm_sometimes / rlm_sometimes.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_sometimes.c
19  * @brief Switches between retuning different return codes.
20  *
21  * @copyright 2012 The FreeRADIUS server project
22  */
23 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28
29 /*
30  *      The instance data for rlm_sometimes is the list of fake values we are
31  *      going to return.
32  */
33 typedef struct rlm_sometimes_t {
34         char                    *rcode_str;
35         int                     rcode;
36         int                     start;
37         int                     end;
38         char                    *key;
39         const DICT_ATTR         *da;
40 } rlm_sometimes_t;
41
42 /*
43  *      A mapping of configuration file names to internal variables.
44  *
45  *      Note that the string is dynamically allocated, so it MUST
46  *      be freed.  When the configuration file parse re-reads the string,
47  *      it free's the old one, and strdup's the new one, placing the pointer
48  *      to the strdup'd string into 'config.string'.  This gets around
49  *      buffer over-flows.
50  */
51 static const CONF_PARSER module_config[] = {
52   { "rcode",      PW_TYPE_STRING_PTR, offsetof(rlm_sometimes_t,rcode_str),
53     NULL, "fail" },
54
55   { "key", PW_TYPE_STRING_PTR,    offsetof(rlm_sometimes_t,key),
56     NULL, "User-Name" },
57
58   { "start", PW_TYPE_INTEGER,    offsetof(rlm_sometimes_t,start),
59     NULL, "0" },
60
61   { "end", PW_TYPE_INTEGER,    offsetof(rlm_sometimes_t,end),
62     NULL, "127" },
63
64   { NULL, -1, 0, NULL, NULL }           /* end the list */
65 };
66
67 static int str2rcode(const char *s)
68 {
69         if(!strcasecmp(s, "reject"))
70                 return RLM_MODULE_REJECT;
71         else if(!strcasecmp(s, "fail"))
72                 return RLM_MODULE_FAIL;
73         else if(!strcasecmp(s, "ok"))
74                 return RLM_MODULE_OK;
75         else if(!strcasecmp(s, "handled"))
76                 return RLM_MODULE_HANDLED;
77         else if(!strcasecmp(s, "invalid"))
78                 return RLM_MODULE_INVALID;
79         else if(!strcasecmp(s, "userlock"))
80                 return RLM_MODULE_USERLOCK;
81         else if(!strcasecmp(s, "notfound"))
82                 return RLM_MODULE_NOTFOUND;
83         else if(!strcasecmp(s, "noop"))
84                 return RLM_MODULE_NOOP;
85         else if(!strcasecmp(s, "updated"))
86                 return RLM_MODULE_UPDATED;
87         else {
88                 radlog(L_ERR,
89                         "rlm_sometimes: Unknown module rcode '%s'.\n", s);
90                 return -1;
91         }
92 }
93
94 static int sometimes_instantiate(CONF_SECTION *conf, void **instance)
95 {
96         rlm_sometimes_t *inst;
97
98         /*
99          *      Set up a storage area for instance data
100          */
101         *instance = inst = talloc_zero(conf, rlm_sometimes_t);
102         if (!inst) return -1;
103
104         /*
105          *      If the configuration parameters can't be parsed, then
106          *      fail.
107          */
108         if (cf_section_parse(conf, inst, module_config) < 0) {
109                 return -1;
110         }
111
112         /*
113          *      Convert the rcode string to an int, and get rid of it
114          */
115         inst->rcode = str2rcode(inst->rcode_str);
116         if (inst->rcode == -1) {
117                 return -1;
118         }
119
120         inst->da = dict_attrbyname(inst->key);
121         if (!inst->da) {
122                 radlog(L_ERR, "rlm_sometimes; Unknown attributes %s", inst->key);
123                 return -1;
124         }
125
126         *instance = inst;
127
128         return 0;
129 }
130
131 /*
132  *      A lie!  It always returns!
133  */
134 static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet,
135                                     RADIUS_PACKET *reply)
136 {
137         uint32_t hash;
138         int value;
139         rlm_sometimes_t *inst = instance;
140         VALUE_PAIR *vp;
141
142         /*
143          *      Set it to NOOP and the module will always do nothing
144          */
145         if (inst->rcode == RLM_MODULE_NOOP) return inst->rcode;
146
147         /*
148          *      Hash based on the given key.  Usually User-Name.
149          */
150         vp = pairfind(packet->vps, inst->da->attr, inst->da->vendor, TAG_ANY);
151         if (!vp) return RLM_MODULE_NOOP;
152
153         hash = fr_hash(&vp->data, vp->length);
154         hash &= 0xff;           /* ensure it's 0..255 */
155         value = hash;
156
157         /*
158          *      Ranges are INCLUSIVE.
159          *      [start,end] returns "rcode"
160          *      Everything else returns "noop"
161          */
162         if (value < inst->start) return RLM_MODULE_NOOP;
163         if (value > inst->end) return RLM_MODULE_NOOP;
164
165         /*
166          *      If we're returning "handled", then set the packet
167          *      code in the reply, so that the server responds.
168          */
169         if ((inst->rcode == RLM_MODULE_HANDLED) && reply) {
170                 switch (packet->code) {
171                 case PW_AUTHENTICATION_REQUEST:
172                         reply->code = PW_AUTHENTICATION_ACK;
173                         break;
174
175                 case PW_ACCOUNTING_REQUEST:
176                         reply->code = PW_ACCOUNTING_RESPONSE;
177                         break;
178
179                 case PW_COA_REQUEST:
180                         reply->code = PW_COA_ACK;
181                         break;
182
183                 case PW_DISCONNECT_REQUEST:
184                         reply->code = PW_DISCONNECT_ACK;
185                         break;
186
187                 default:
188                         break;
189                 }
190         }
191
192         return inst->rcode;
193 }
194
195 static rlm_rcode_t sometimes_packet(void *instance, REQUEST *request)
196 {
197         return sometimes_return(instance, request->packet, request->reply);
198 }
199
200 static rlm_rcode_t sometimes_reply(void *instance, REQUEST *request)
201 {
202         return sometimes_return(instance, request->reply, NULL);
203 }
204
205 static rlm_rcode_t sometimes_pre_proxy(void *instance, REQUEST *request)
206 {
207         if (!request->proxy) return RLM_MODULE_NOOP;
208
209         return sometimes_return(instance, request->proxy, request->proxy_reply);
210 }
211
212 static rlm_rcode_t sometimes_post_proxy(void *instance, REQUEST *request)
213 {
214         if (!request->proxy_reply) return RLM_MODULE_NOOP;
215
216         return sometimes_return(instance, request->proxy_reply, NULL);
217 }
218
219 module_t rlm_sometimes = {
220         RLM_MODULE_INIT,
221         "sometimes",
222         RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
223         sometimes_instantiate,          /* instantiation */
224         NULL,                           /* detach */
225         {
226                 sometimes_packet,       /* authentication */
227                 sometimes_packet,       /* authorization */
228                 sometimes_packet,       /* preaccounting */
229                 sometimes_packet,       /* accounting */
230                 NULL,
231                 sometimes_pre_proxy,    /* pre-proxy */
232                 sometimes_post_proxy,   /* post-proxy */
233                 sometimes_reply         /* post-auth */
234 #ifdef WITH_COA
235                 ,
236                 sometimes_packet,       /* recv-coa */
237                 sometimes_reply         /* send-coa */
238 #endif
239         },
240 };