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