11680261fc01611a18d7bfc1dc8cef0728c70560
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * FIXME add copyrights
22  */
23
24 #include "autoconf.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #include "libradius.h"
39 #include "radiusd.h"
40 #include "modules.h"
41
42 static const char rcsid[] = "$Id$";
43
44 #define  REALM_FORMAT_PREFIX   0
45 #define  REALM_FORMAT_SUFFIX   1
46
47 typedef struct realm_config_t {
48         int        format;
49         char       *formatstring;
50         char       *delim;
51 } realm_config_t;
52
53 static CONF_PARSER module_config[] = {
54   { "format", PW_TYPE_STRING_PTR,
55     offsetof(realm_config_t,formatstring), NULL, "suffix" },
56   { "delimiter", PW_TYPE_STRING_PTR,
57     offsetof(realm_config_t,delim), NULL, "@" },
58   { NULL, -1, 0, NULL, NULL }    /* end the list */
59 };
60
61 /*
62  *      Internal function to cut down on duplicated code.
63  *
64  *      Returns NULL on don't proxy, realm otherwise.
65  */
66 static REALM *check_for_realm(void *instance, REQUEST *request)
67 {
68         char namebuf[MAX_STRING_LEN];
69         char *username;
70         char *realmname = NULL;
71         char *ptr;
72         VALUE_PAIR *vp;
73         REALM *realm;
74
75         struct realm_config_t *inst = instance;
76
77         /*
78          *      If the request has a proxy entry, then it's a proxy
79          *      reply, and we're walking through the module list again.
80          *
81          *      In that case, don't bother trying to proxy the request
82          *      again.
83          *
84          *      Also, if there's no User-Name attribute, we can't
85          *      proxy it, either.
86          */
87         if ((request->proxy != NULL) ||
88             (request->username == NULL)) {
89                 DEBUG2("    rlm_realm: Proxy reply, or no User-Name.  Ignoring.");
90                 return NULL;
91         }
92
93         /*
94          *      Check for 'Realm' attribute.  If it exists, then we've proxied
95          *      it already ( via another rlm_realm instance ) and should return.
96          */
97
98         if ( (vp = pairfind(request->packet->vps, PW_REALM)) != NULL ) {
99                 DEBUG2("    rlm_realm: Request already proxied.  Ignoring.");
100                 return NULL;
101         }
102
103         /*
104          *      We will be modifing this later, so we want our own copy
105          *      of it.
106          */
107         strNcpy(namebuf, (char *)request->username->strvalue, sizeof(namebuf));
108         username = namebuf;
109
110         switch(inst->format)
111         {
112
113         case REALM_FORMAT_SUFFIX:
114           
115           /* DEBUG2("  rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
116                 realmname = strrchr(username, inst->delim[0]);          
117                 if (realmname) {
118                         *realmname = '\0';
119                         realmname++;
120                 }
121                 break;
122                 
123         case REALM_FORMAT_PREFIX:
124                 
125                 /* DEBUG2("  rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
126                 
127                 ptr = strchr(username, inst->delim[0]);
128                 if (ptr) {
129                         *ptr = '\0';
130                      ptr++;
131                      realmname = username;
132                      username = ptr;
133                 }
134                 break;
135                
136         default:
137                 realmname = NULL;
138                 break;
139         }
140
141         /*
142          *      Print out excruciatingly descriptive debugging messages
143          *      for the people who find it too difficult to think about
144          *      what's going on.
145          */
146         if (realmname) {
147                 DEBUG2("    rlm_realm: Looking up realm \"%s\" for User-Name = \"%s\"",
148                        realmname, request->username->strvalue);
149         } else {
150                 DEBUG2("    rlm_realm: No '%c' in User-Name = \"%s\", looking up realm NULL",
151                        inst->delim[0], request->username->strvalue);
152         }
153
154         /*
155          *      Allow NULL realms.
156          */
157         realm = realm_find(realmname, (request->packet->code == PW_ACCOUNTING_REQUEST));
158         if (!realm) {
159                 DEBUG2("    rlm_realm: No such realm \"%s\"",
160                        (realmname == NULL) ? "NULL" : realmname);
161                 return NULL;
162         }
163         DEBUG2("    rlm_realm: Found realm \"%s\"", realm->realm);
164         
165         /*
166          *      If we've been told to strip the realm off, then do so.
167          */
168         if (realm->striprealm) {
169                 /*
170                  *      Create the Stripped-User-Name attribute, if it
171                  *      doesn't exist.
172                  *
173                  */
174                 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
175                         vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
176                         if (!vp) {
177                                 radlog(L_ERR|L_CONS, "no memory");
178                                 exit(1);
179                         }
180                         pairadd(&request->packet->vps, vp);
181                         DEBUG2("    rlm_realm: Adding Stripped-User-Name = \"%s\"", username);
182                 } else {
183                         vp = request->username;
184                         DEBUG2("    rlm_realm: Setting Stripped-User-Name = \"%s\"", username);
185                 }
186
187                 strcpy(vp->strvalue, username);
188                 vp->length = strlen((char *)vp->strvalue);
189                 request->username = vp;
190         }
191
192         DEBUG2("    rlm_realm: Proxying request from user %s to realm %s",
193                username, realm->realm);
194
195         /*
196          *      Add the realm name to the request.
197          */
198         pairadd(&request->packet->vps, pairmake("Realm", realm->realm,
199                                                 T_OP_EQ));
200         DEBUG2("    rlm_realm: Adding Realm = \"%s\"", realm->realm);
201
202         /*
203          *      Figure out what to do with the request.
204          */
205         switch (request->packet->code) {
206         default:
207                 DEBUG2("    rlm_realm: Unknown packet code %d\n",
208                        request->packet->code);
209                 return NULL;            /* don't do anything */
210                 
211                 /*
212                  *      Perhaps accounting proxying was turned off.
213                  */
214         case PW_ACCOUNTING_REQUEST:
215                 if (realm->acct_ipaddr == htonl(INADDR_NONE)) {
216                         DEBUG2("    rlm_realm: Accounting realm is LOCAL.");
217                         return NULL;
218                 }
219
220                 if (realm->acct_port == 0) {
221                         DEBUG2("    rlm_realm: acct_port is not set.  Proxy cancelled.");
222                         return NULL;
223                 }
224                 break;
225
226                 /*
227                  *      Perhaps authentication proxying was turned off.
228                  */
229         case PW_AUTHENTICATION_REQUEST:
230                 if (realm->ipaddr == htonl(INADDR_NONE)) {
231                         DEBUG2("    rlm_realm: Authentication realm is LOCAL.");
232                         return NULL;
233                 }
234
235                 if (realm->auth_port == 0) {
236                         DEBUG2("    rlm_realm: auth_port is not set.  Proxy cancelled.");
237                         return NULL;
238                 }
239                 break;
240         }
241
242         /*
243          *      If this request has arrived from another freeradius server
244          *      that has already proxied the request, we don't need to do
245          *      it again.
246          */
247         for (vp = request->packet->vps; vp; vp = vp->next) {
248                 if (vp->attribute == PW_FREERADIUS_PROXIED_TO) {
249                         if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
250                             vp->lvalue == realm->ipaddr) {
251                                 DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
252                                 return NULL;
253                         }
254                         if (request->packet->code == PW_ACCOUNTING_REQUEST &&
255                             vp->lvalue == realm->acct_ipaddr) {
256                                 DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
257                                 return NULL;
258                         }
259                 }
260         }
261
262         return realm;
263 }
264
265 /*
266  *      Add a "Proxy-To-Realm" attribute to the request.
267  */
268 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
269 {
270         VALUE_PAIR *vp;
271
272         /*
273          *      Tell the server to proxy this request to another
274          *      realm.
275          */
276         vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
277         if (!vp) {
278                 radlog(L_ERR|L_CONS, "no memory");
279                 exit(1);
280         }
281         
282         /*
283          *  Add it, even if it's already present.
284          */
285         pairadd(vps, vp);
286 }
287
288 /*
289  *  Perform the realm module instantiation.  Configuration info is
290  *  stored in *instance for later use.
291  */
292
293 static int realm_instantiate(CONF_SECTION *conf, void **instance)
294 {
295         struct realm_config_t *inst;
296
297         /* setup a storage area for instance data */
298         inst = rad_malloc(sizeof(*inst));
299         if (!inst) {
300                 return -1;
301         }
302         memset(inst, 0, sizeof(*inst));
303
304         if(cf_section_parse(conf, inst, module_config) < 0) {
305                free(inst);
306                return -1;
307         }
308
309         if(strcasecmp(inst->formatstring, "suffix") == 0) {
310              inst->format = REALM_FORMAT_SUFFIX;
311         } else if(strcasecmp(inst->formatstring, "prefix") == 0) {
312              inst->format = REALM_FORMAT_PREFIX;
313         } else {
314              radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
315              free(inst);
316              return -1;
317         }
318         free(inst->formatstring);
319         if(strlen(inst->delim) != 1) {
320              radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
321              free(inst);
322              return -1;
323         }
324
325         *instance = inst;
326         return 0;
327
328 }
329
330
331
332  
333
334 /*
335  *  Examine a request for a username with an realm, and if it
336  *  corresponds to something in the realms file, set that realm as
337  *  Proxy-To.
338  *
339  *  This should very nearly duplicate the old proxy_send() code
340  */
341 static int realm_authorize(void *instance, REQUEST *request)
342 {
343         REALM *realm;
344
345         /*
346          *      Check if we've got to proxy the request.
347          *      If not, return without adding a Proxy-To-Realm
348          *      attribute.
349          */
350         realm = check_for_realm(instance, request);
351         if (!realm) {
352                 return RLM_MODULE_NOOP;
353         }
354
355         /*
356          *      Maybe add a Proxy-To-Realm attribute to the request.
357          */
358         DEBUG2("    rlm_realm: Preparing to proxy authentication request to realm \"%s\"\n",
359                realm->realm);
360         add_proxy_to_realm(&request->config_items, realm);
361
362         return RLM_MODULE_UPDATED; /* try the next module */
363 }
364
365 /*
366  * This does the exact same thing as the realm_authorize, it's just called
367  * differently.
368  */
369 static int realm_preacct(void *instance, REQUEST *request)
370 {
371         const char *name = (char *)request->username->strvalue;
372         REALM *realm;
373
374         if (!name)
375           return RLM_MODULE_OK;
376         
377
378         /*
379          *      Check if we've got to proxy the request.
380          *      If not, return without adding a Proxy-To-Realm
381          *      attribute.
382          */
383         realm = check_for_realm(instance, request);
384         if (!realm) {
385                 return RLM_MODULE_NOOP;
386         }
387
388
389         /*
390          *      Maybe add a Proxy-To-Realm attribute to the request.
391          */
392         DEBUG2("    rlm_realm: Preparing to proxy accounting request to realm \"%s\"\n",
393                realm->realm);
394         add_proxy_to_realm(&request->config_items, realm);
395
396         return RLM_MODULE_UPDATED; /* try the next module */
397 }
398
399 static int realm_detach(void *instance)
400 {
401         struct realm_config_t *inst = instance;
402         free(inst->delim);
403         free(instance);
404         return 0;
405 }
406
407 /* globally exported name */
408 module_t rlm_realm = {
409   "realm",
410   0,                            /* type: reserved */
411   NULL,                         /* initialization */
412   realm_instantiate,            /* instantiation */
413   {
414           NULL,                 /* authentication */
415           realm_authorize,      /* authorization */
416           realm_preacct,        /* preaccounting */
417           NULL,                 /* accounting */
418           NULL,                 /* checksimul */
419           NULL,                 /* pre-proxy */
420           NULL,                 /* post-proxy */
421           NULL                  /* post-auth */
422   },
423   realm_detach,                 /* detach */
424   NULL,                         /* destroy */
425 };