Added or cleaned up copyright and license info. Cross-check me!
[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 #include "libradius.h"
31 #include "radiusd.h"
32 #include "modules.h"
33
34 static const char rcsid[] = "$Id$";
35
36 #define  REALM_FORMAT_PREFIX   0
37 #define  REALM_FORMAT_SUFFIX   1
38
39 typedef struct realm_config_t {
40         int        format;
41         char       *formatstring;
42         char       *delim;
43 } realm_config_t;
44
45 static realm_config_t config;
46
47 static CONF_PARSER module_config[] = {
48   { "format", PW_TYPE_STRING_PTR, &config.formatstring, "suffix" },
49   { "delimiter", PW_TYPE_STRING_PTR, &config.delim, "@" },
50   { NULL, -1, NULL, NULL }    /* end the list */
51 };
52
53 /*
54  *      Internal function to cut down on duplicated code.
55  *
56  *      Returns NULL on don't proxy, realm otherwise.
57  */
58 static REALM *check_for_realm(void *instance, REQUEST *request)
59 {
60         char namebuf[MAX_STRING_LEN];
61         char *username;
62         char *realmname = (char *)NULL;
63         char *ptr;
64         VALUE_PAIR *vp;
65         REALM *realm;
66
67         struct realm_config_t *inst = instance;
68
69         namebuf[0] = '\0';
70         username = namebuf;
71
72         /*
73          *      If the request has a proxy entry, then it's a proxy
74          *      reply, and we're walking through the module list again.
75          *
76          *      In that case, don't bother trying to proxy the request
77          *      again.
78          *
79          *      Also, if there's no User-Name attribute, we can't
80          *      proxy it, either.
81          */
82         if ((request->proxy != NULL) ||
83             (request->username == NULL)) {
84                 return NULL;
85         }
86
87         strncpy(namebuf, request->username->strvalue, MAX_STRING_LEN-1);
88
89         switch(inst->format)
90         {
91
92         case REALM_FORMAT_SUFFIX:
93           
94           /* DEBUG2("  rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
95
96                 realmname = strchr(username, inst->delim[0]);           
97                 if (realmname) {
98                         *realmname = '\0';
99                         realmname++;
100                 }
101                 break;
102                 
103         case REALM_FORMAT_PREFIX:
104                 
105                 /* DEBUG2("  rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
106                 
107                 ptr = strchr(username, inst->delim[0]);
108                 if (ptr) {
109                         *ptr = '\0';
110                      ptr++;
111                      realmname = username;
112                      username = ptr;    
113                 }
114                 break;
115                
116         default:
117                 realmname = NULL;
118                 break;
119         }
120
121         /*
122          *      Allow NULL realms.
123          */
124         realm = realm_find(realmname);
125         if (!realm) {
126                 return NULL;
127         }
128         
129         /* make sure it's proxyable realm */
130         if (realm->notrealm) {
131                 return NULL;
132         }
133
134         DEBUG2("  rlm_realm: Proxying request from user %s to realm %s",
135                username, realm->realm);
136
137         /*
138          *      If we've been told to strip the realm off, then do so.
139          */
140         if (realm->striprealm) {
141                 /*
142                  *      Create the Stripped-User-Name attribute, if it
143                  *      doesn't exist.
144                  *
145                  */
146                 vp = pairfind(request->packet->vps, PW_STRIPPED_USER_NAME);
147                 if (!vp) {
148                         vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
149                         if (!vp) {
150                                 radlog(L_ERR|L_CONS, "no memory");
151                                 exit(1);
152                         }
153                         strcpy(vp->strvalue, username);
154                         vp->length = strlen((char *)vp->strvalue);
155                         pairadd(&request->packet->vps, vp);
156                         request->username = vp;
157                 }
158         }
159
160         /*
161          *      Don't add a 'Realm' attribute, proxy.c does
162          *      that for us.
163          */
164
165         /*
166          *      Perhaps accounting proxying was turned off.
167          */
168         if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
169             (realm->acct_port == 0)) {
170                 /* log a warning that the packet isn't getting proxied ??? */
171                 DEBUG2("rlm_realm:  acct_port is not set.  proxy cancelled");
172                 return NULL;
173         }
174
175         /*
176          *      Perhaps authentication proxying was turned off.
177          */
178         if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
179             (realm->auth_port == 0)) {
180                 /* log a warning that the packet isn't getting proxied ??? */
181                 DEBUG2("rlm_realm:  auth_port is not set.  proxy cancelled");
182                 return NULL;
183         }
184
185
186         return realm;
187 }
188
189 /*
190  *      Maybe add a "Proxy-To-Realm" attribute to the request.
191  *
192  *      If it's a LOCAL realm, then don't bother.
193  */
194 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
195 {
196         VALUE_PAIR *vp;
197
198         /*
199          *      If it's the LOCAL realm, we do NOT proxy it, but
200          *      we DO strip the User-Name, if told to do so.
201          */
202         if (strcmp(realm->server, "LOCAL") == 0) {
203                 return;
204         }
205
206         /*
207          *      Tell the server to proxy this request to another
208          *      realm.
209          */
210         vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
211         if (!vp) {
212                 radlog(L_ERR|L_CONS, "no memory");
213                 exit(1);
214         }
215         
216         /*
217          *  Add it, even if it's already present.
218          */
219         pairadd(vps, vp);
220 }
221
222 /*
223  *  Perform the realm module instantiation.  Configuration info is
224  *  stored in *instance for later use.
225  */
226
227 static int realm_instantiate(CONF_SECTION *conf, void **instance)
228 {
229         struct realm_config_t *inst;
230
231         /* setup a storage area for instance data */
232         inst = malloc(sizeof(struct realm_config_t));
233         if(!inst) {
234                radlog(L_ERR|L_CONS, "Out of memory\n");
235                return -1;
236         }
237
238         if(cf_section_parse(conf, module_config) < 0) {
239                free(inst);
240                return -1;
241         }
242
243         /* copy the configuration info into the instance data */
244
245         if(strcasecmp(config.formatstring, "suffix") == 0) {
246              inst->format = REALM_FORMAT_SUFFIX;
247         } else if(strcasecmp(config.formatstring, "prefix") == 0) {
248              inst->format = REALM_FORMAT_PREFIX;
249         } else {
250              radlog(L_ERR, "Bad value \"%s\" for realm format value", config.formatstring);
251              free(inst);
252              return -1;
253         }
254         if(strlen(config.delim) != 1) {
255              radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", config.delim);
256              free(inst);
257              return -1;
258         }
259         inst->delim = config.delim;
260
261         /* set these to NULL to prevent other instances from reusing the data */
262
263         config.formatstring = NULL;
264         config.delim = NULL;
265
266         *instance = inst;
267         return 0;
268
269 }
270
271
272
273  
274
275 /*
276  *  Examine a request for a username with an realm, and if it
277  *  corresponds to something in the realms file, set that realm as
278  *  Proxy-To.
279  *
280  *  This should very nearly duplicate the old proxy_send() code
281  */
282 static int realm_authorize(void *instance, REQUEST *request)
283 {
284         REALM *realm;
285
286         /*
287          *      Check if we've got to proxy the request.
288          *      If not, return without adding a Proxy-To-Realm
289          *      attribute.
290          */
291         realm = check_for_realm(instance, request);
292         if (!realm) {
293                 return RLM_MODULE_OK;
294         }
295
296         /*
297          *      Maybe add a Proxy-To-Realm attribute to the request.
298          */
299         add_proxy_to_realm(&request->config_items, realm);
300
301         return RLM_MODULE_UPDATED; /* try the next module */
302 }
303
304 /*
305  * This does the exact same thing as the realm_authorize, it's just called
306  * differently.
307  */
308 static int realm_preacct(void *instance, REQUEST *request)
309 {
310         const char *name = (char *)request->username->strvalue;
311         REALM *realm;
312
313         if (!name)
314           return RLM_MODULE_OK;
315         
316
317         /*
318          *      Check if we've got to proxy the request.
319          *      If not, return without adding a Proxy-To-Realm
320          *      attribute.
321          */
322         realm = check_for_realm(instance, request);
323         if (!realm) {
324                 return RLM_MODULE_OK;
325         }
326
327
328         /*
329          *      Maybe add a Proxy-To-Realm attribute to the request.
330          */
331         add_proxy_to_realm(&request->config_items, realm);
332
333         return RLM_MODULE_OK; /* try the next module */
334 }
335
336 /* globally exported name */
337 module_t rlm_realm = {
338   "realm",
339   0,                            /* type: reserved */
340   NULL,                         /* initialization */
341   realm_instantiate,            /* instantiation */
342   realm_authorize,              /* authorization */
343   NULL,                         /* authentication */
344   realm_preacct,                /* preaccounting */
345   NULL,                         /* accounting */
346   NULL,                         /* checksimul */
347   NULL,                         /* detach */
348   NULL,                         /* destroy */
349 };