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