Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_realm / rlm_realm.c
1 /*
2  * rlm_realm.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 2000,2006  The FreeRADIUS server project
21  * FIXME add copyrights
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 #define  REALM_FORMAT_PREFIX   0
31 #define  REALM_FORMAT_SUFFIX   1
32
33 typedef struct realm_config_t {
34         int        format;
35         char       *formatstring;
36         char       *delim;
37         int        ignore_default;
38         int        ignore_null;
39 } realm_config_t;
40
41 static CONF_PARSER module_config[] = {
42   { "format", PW_TYPE_STRING_PTR,
43     offsetof(realm_config_t,formatstring), NULL, "suffix" },
44   { "delimiter", PW_TYPE_STRING_PTR,
45     offsetof(realm_config_t,delim), NULL, "@" },
46   { "ignore_default", PW_TYPE_BOOLEAN,
47     offsetof(realm_config_t,ignore_default), NULL, "no" },
48   { "ignore_null", PW_TYPE_BOOLEAN,
49     offsetof(realm_config_t,ignore_null), NULL, "no" },
50   { NULL, -1, 0, NULL, NULL }    /* end the list */
51 };
52
53 /*
54  *      Internal function to cut down on duplicated code.
55  *
56  *      Returns -1 on failure, 0 on no failure.  returnrealm
57  *      is NULL on don't proxy, realm otherwise.
58  */
59 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
60 {
61         char namebuf[MAX_STRING_LEN];
62         char *username;
63         char *realmname = NULL;
64         char *ptr;
65         VALUE_PAIR *vp;
66         REALM *realm;
67
68         struct realm_config_t *inst = instance;
69
70         /* initiate returnrealm */
71         *returnrealm = NULL;
72
73         /*
74          *      If the request has a proxy entry, then it's a proxy
75          *      reply, and we're walking through the module list again.
76          *
77          *      In that case, don't bother trying to proxy the request
78          *      again.
79          *
80          *      Also, if there's no User-Name attribute, we can't
81          *      proxy it, either.
82          */
83 #ifdef WITH_PROXY
84         if ((request->proxy != NULL) ||
85             (request->username == NULL)) {
86                 RDEBUG2("Proxy reply, or no User-Name.  Ignoring.");
87                 return RLM_MODULE_OK;
88         }
89 #endif
90
91         /*
92          *      Check for 'Realm' attribute.  If it exists, then we've proxied
93          *      it already ( via another rlm_realm instance ) and should return.
94          */
95
96         if (pairfind(request->packet->vps, PW_REALM, 0) != NULL ) {
97                 RDEBUG2("Request already proxied.  Ignoring.");
98                 return RLM_MODULE_OK;
99         }
100
101         /*
102          *      We will be modifing this later, so we want our own copy
103          *      of it.
104          */
105         strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
106         username = namebuf;
107
108         switch(inst->format)
109         {
110
111         case REALM_FORMAT_SUFFIX:
112
113           /* DEBUG2("  rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
114                 realmname = strrchr(username, inst->delim[0]);
115                 if (realmname) {
116                         *realmname = '\0';
117                         realmname++;
118                 }
119                 break;
120
121         case REALM_FORMAT_PREFIX:
122
123                 /* DEBUG2("  rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
124
125                 ptr = strchr(username, inst->delim[0]);
126                 if (ptr) {
127                         *ptr = '\0';
128                      ptr++;
129                      realmname = username;
130                      username = ptr;
131                 }
132                 break;
133
134         default:
135                 realmname = NULL;
136                 break;
137         }
138
139         /*
140          *      Print out excruciatingly descriptive debugging messages
141          *      for the people who find it too difficult to think about
142          *      what's going on.
143          */
144         if (realmname) {
145                 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
146                        realmname, request->username->vp_strvalue);
147         } else {
148                 if( inst->ignore_null ) {
149                         RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
150                         inst->delim[0], request->username->vp_strvalue);
151                         return RLM_MODULE_NOOP;
152                 }
153                 RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
154                        inst->delim[0], request->username->vp_strvalue);
155         }
156
157         /*
158          *      Allow DEFAULT realms unless told not to.
159          */
160         realm = realm_find(realmname);
161         if (!realm) {
162                 RDEBUG2("No such realm \"%s\"",
163                        (realmname == NULL) ? "NULL" : realmname);
164                 return RLM_MODULE_NOOP;
165         }
166         if( inst->ignore_default &&
167             (strcmp(realm->name, "DEFAULT")) == 0) {
168                 RDEBUG2("Found DEFAULT, but skipping due to config.");
169                 return RLM_MODULE_NOOP;
170         }
171
172         RDEBUG2("Found realm \"%s\"", realm->name);
173
174         /*
175          *      If we've been told to strip the realm off, then do so.
176          */
177         if (realm->striprealm) {
178                 /*
179                  *      Create the Stripped-User-Name attribute, if it
180                  *      doesn't exist.
181                  *
182                  */
183                 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
184                         vp = radius_paircreate(request, &request->packet->vps,
185                                                PW_STRIPPED_USER_NAME, 0,
186                                                PW_TYPE_STRING);
187                         RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
188                 } else {
189                         vp = request->username;
190                         RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
191                 }
192
193                 strcpy(vp->vp_strvalue, username);
194                 vp->length = strlen((char *)vp->vp_strvalue);
195                 request->username = vp;
196         }
197
198         /*
199          *      Add the realm name to the request.
200          *      If the realm is a regex, the use the realm as entered
201          *      by the user.  Otherwise, use the configured realm name,
202          *      as realm name comparison is case insensitive.  We want
203          *      to use the configured name, rather than what the user
204          *      entered.
205          */
206         if (realm->name[0] != '~') realmname = realm->name;
207         pairadd(&request->packet->vps, pairmake("Realm", realmname,
208                                                 T_OP_EQ));
209         RDEBUG2("Adding Realm = \"%s\"", realmname);
210
211         /*
212          *      Figure out what to do with the request.
213          */
214         switch (request->packet->code) {
215         default:
216                 RDEBUG2("Unknown packet code %d\n",
217                        request->packet->code);
218                 return RLM_MODULE_OK;           /* don't do anything */
219
220                 /*
221                  *      Perhaps accounting proxying was turned off.
222                  */
223         case PW_ACCOUNTING_REQUEST:
224                 if (!realm->acct_pool) {
225                         RDEBUG2("Accounting realm is LOCAL.");
226                         return RLM_MODULE_OK;
227                 }
228                 break;
229
230                 /*
231                  *      Perhaps authentication proxying was turned off.
232                  */
233         case PW_AUTHENTICATION_REQUEST:
234                 if (!realm->auth_pool) {
235                         RDEBUG2("Authentication realm is LOCAL.");
236                         return RLM_MODULE_OK;
237                 }
238                 break;
239         }
240
241 #ifdef WITH_PROXY
242         RDEBUG2("Proxying request from user %s to realm %s",
243                username, realm->name);
244
245         /*
246          *      Skip additional checks if it's not an accounting
247          *      request.
248          */
249         if (request->packet->code != PW_ACCOUNTING_REQUEST) {
250                 *returnrealm = realm;
251                 return RLM_MODULE_UPDATED;
252         }
253
254         /*
255          *      FIXME: Each server should have a unique server key,
256          *      and put it in the accounting packet.  Every server
257          *      should know about the keys, and NOT proxy requests to
258          *      a server with key X if the packet already contains key
259          *      X.
260          */
261
262         /*
263          *      If this request has arrived from another freeradius server
264          *      that has already proxied the request, we don't need to do
265          *      it again.
266          */
267         vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0);
268         if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
269                 int i;
270                 fr_ipaddr_t my_ipaddr;
271
272                 my_ipaddr.af = AF_INET;
273                 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
274
275                 /*
276                  *      Loop over the home accounting servers for this
277                  *      realm.  If one of them has the same IP as the
278                  *      FreeRADIUS-Proxied-To attribute, then the
279                  *      packet has already been sent there.  Don't
280                  *      send it there again.
281                  */
282                 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
283                         if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
284                                             &my_ipaddr) == 0) {
285                                 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
286                                 return RLM_MODULE_OK;
287                         }
288                 }
289
290                 /*
291                  *      See detail_recv() in src/main/listen.c for the
292                  *      additional checks.
293                  */
294         } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
295                    ((request->packet->src_ipaddr.af == AF_INET6) ||
296                     (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)))) {
297                 int i;
298
299                 /*
300                  *      Loop over the home accounting servers for this
301                  *      realm.  If one of them has the same IP as the
302                  *      FreeRADIUS-Proxied-To attribute, then the
303                  *      packet has already been sent there.  Don't
304                  *      send it there again.
305                  */
306                 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
307                         if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
308                                              &request->packet->src_ipaddr) == 0) &&
309                             (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
310                                 RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
311                                 return RLM_MODULE_OK;
312                         }
313                 }
314
315         }
316 #endif
317
318         /*
319          *      We got this far, which means we have a realm, set returnrealm
320          */
321         *returnrealm = realm;
322
323         return RLM_MODULE_UPDATED;
324 }
325
326 /*
327  *      Add a "Proxy-To-Realm" attribute to the request.
328  */
329 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
330 {
331         VALUE_PAIR *vp;
332
333         /*
334          *      Tell the server to proxy this request to another
335          *      realm.
336          */
337         vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
338         if (!vp) {
339                 radlog(L_ERR|L_CONS, "no memory");
340                 exit(1);
341         }
342
343         /*
344          *  Add it, even if it's already present.
345          */
346         pairadd(vps, vp);
347 }
348
349 /*
350  *  Perform the realm module instantiation.  Configuration info is
351  *  stored in *instance for later use.
352  */
353
354 static int realm_instantiate(CONF_SECTION *conf, void **instance)
355 {
356         struct realm_config_t *inst;
357
358         /* setup a storage area for instance data */
359         inst = rad_malloc(sizeof(*inst));
360         if (!inst) {
361                 return -1;
362         }
363         memset(inst, 0, sizeof(*inst));
364
365         if(cf_section_parse(conf, inst, module_config) < 0) {
366                free(inst);
367                return -1;
368         }
369
370         if(strcasecmp(inst->formatstring, "suffix") == 0) {
371              inst->format = REALM_FORMAT_SUFFIX;
372         } else if(strcasecmp(inst->formatstring, "prefix") == 0) {
373              inst->format = REALM_FORMAT_PREFIX;
374         } else {
375              radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
376              free(inst);
377              return -1;
378         }
379         if(strlen(inst->delim) != 1) {
380              radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
381              free(inst);
382              return -1;
383         }
384
385         *instance = inst;
386         return 0;
387
388 }
389
390
391
392
393
394 /*
395  *  Examine a request for a username with an realm, and if it
396  *  corresponds to something in the realms file, set that realm as
397  *  Proxy-To.
398  *
399  *  This should very nearly duplicate the old proxy_send() code
400  */
401 static int realm_authorize(void *instance, REQUEST *request)
402 {
403         int rcode;
404         REALM *realm;
405
406         /*
407          *      Check if we've got to proxy the request.
408          *      If not, return without adding a Proxy-To-Realm
409          *      attribute.
410          */
411         rcode = check_for_realm(instance, request, &realm);
412         if (rcode != RLM_MODULE_UPDATED) return rcode;
413         if (!realm) return RLM_MODULE_NOOP;
414
415         /*
416          *      Maybe add a Proxy-To-Realm attribute to the request.
417          */
418         RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
419                realm->name);
420         add_proxy_to_realm(&request->config_items, realm);
421
422         return RLM_MODULE_UPDATED; /* try the next module */
423 }
424
425 /*
426  * This does the exact same thing as the realm_authorize, it's just called
427  * differently.
428  */
429 static int realm_preacct(void *instance, REQUEST *request)
430 {
431         int rcode;
432         const char *name = (char *)request->username->vp_strvalue;
433         REALM *realm;
434
435         if (!name)
436           return RLM_MODULE_OK;
437
438
439         /*
440          *      Check if we've got to proxy the request.
441          *      If not, return without adding a Proxy-To-Realm
442          *      attribute.
443          */
444         rcode = check_for_realm(instance, request, &realm);
445         if (rcode != RLM_MODULE_UPDATED) return rcode;
446         if (!realm) return RLM_MODULE_NOOP;
447
448         /*
449          *      Maybe add a Proxy-To-Realm attribute to the request.
450          */
451         RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
452                realm->name);
453         add_proxy_to_realm(&request->config_items, realm);
454
455         return RLM_MODULE_UPDATED; /* try the next module */
456 }
457
458 static int realm_detach(void *instance)
459 {
460         free(instance);
461         return 0;
462 }
463
464 /* globally exported name */
465 module_t rlm_realm = {
466         RLM_MODULE_INIT,
467         "realm",
468         RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
469         realm_instantiate,              /* instantiation */
470         realm_detach,                   /* detach */
471         {
472                 NULL,                   /* authentication */
473                 realm_authorize,        /* authorization */
474                 realm_preacct,  /* preaccounting */
475                 NULL,                   /* accounting */
476                 NULL,                   /* checksimul */
477                 NULL,                   /* pre-proxy */
478                 NULL,                   /* post-proxy */
479                 NULL                    /* post-auth */
480         },
481 };