Massively cleaned up #include's, so they're in a consistent
[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         if (request->packet->src_ipaddr.af != AF_INET) {
74                 DEBUG2("rlm_realm: IPv6 is not supported!");
75                 return 0;
76         }
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 0;
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 0;
102         }
103
104         /*
105          *      We will be modifing this later, so we want our own copy
106          *      of it.
107          */
108         strlcpy(namebuf, (char *)request->username->vp_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->vp_strvalue);
150         } else {
151                 if( inst->ignore_null ) {
152                         DEBUG2("    rlm_realm: No '%c' in User-Name = \"%s\", skipping NULL due to config.",
153                         inst->delim[0], request->username->vp_strvalue);
154                         return 0;
155                 }
156                 DEBUG2("    rlm_realm: No '%c' in User-Name = \"%s\", looking up realm NULL",
157                        inst->delim[0], request->username->vp_strvalue);
158         }
159
160         /*
161          *      Allow DEFAULT realms unless told not to.
162          */
163         realm = realm_find(realmname);
164         if (!realm) {
165                 DEBUG2("    rlm_realm: No such realm \"%s\"",
166                        (realmname == NULL) ? "NULL" : realmname);
167                 return 0;
168         }
169         if( inst->ignore_default &&
170             (strcmp(realm->name, "DEFAULT")) == 0) {
171                 DEBUG2("    rlm_realm: Found DEFAULT, but skipping due to config.");
172                 return 0;
173         }
174
175
176         DEBUG2("    rlm_realm: Found realm \"%s\"", realm->name);
177
178         /*
179          *      If we've been told to strip the realm off, then do so.
180          */
181         if (realm->striprealm) {
182                 /*
183                  *      Create the Stripped-User-Name attribute, if it
184                  *      doesn't exist.
185                  *
186                  */
187                 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
188                         vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
189                         if (!vp) {
190                                 radlog(L_ERR|L_CONS, "no memory");
191                                 return -1;
192                         }
193                         pairadd(&request->packet->vps, vp);
194                         DEBUG2("    rlm_realm: Adding Stripped-User-Name = \"%s\"", username);
195                 } else {
196                         vp = request->username;
197                         DEBUG2("    rlm_realm: Setting Stripped-User-Name = \"%s\"", username);
198                 }
199
200                 strcpy(vp->vp_strvalue, username);
201                 vp->length = strlen((char *)vp->vp_strvalue);
202                 request->username = vp;
203         }
204
205         DEBUG2("    rlm_realm: Proxying request from user %s to realm %s",
206                username, realm->name);
207
208         /*
209          *      Add the realm name to the request.
210          */
211         pairadd(&request->packet->vps, pairmake("Realm", realm->name,
212                                                 T_OP_EQ));
213         DEBUG2("    rlm_realm: Adding Realm = \"%s\"", realm->name);
214
215         /*
216          *      Figure out what to do with the request.
217          */
218         switch (request->packet->code) {
219         default:
220                 DEBUG2("    rlm_realm: Unknown packet code %d\n",
221                        request->packet->code);
222                 return 0;               /* don't do anything */
223
224                 /*
225                  *      Perhaps accounting proxying was turned off.
226                  */
227         case PW_ACCOUNTING_REQUEST:
228                 if (!realm->acct_pool) {
229                         DEBUG2("    rlm_realm: Accounting realm is LOCAL.");
230                         return 0;
231                 }
232                 break;
233
234                 /*
235                  *      Perhaps authentication proxying was turned off.
236                  */
237         case PW_AUTHENTICATION_REQUEST:
238                 if (!realm->auth_pool) {
239                         DEBUG2("    rlm_realm: Authentication realm is LOCAL.");
240                         return 0;
241                 }
242                 break;
243         }
244
245         /*
246          *      If this request has arrived from another freeradius server
247          *      that has already proxied the request, we don't need to do
248          *      it again.
249          */
250         vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO);
251         if (vp) {
252 #if 0
253                 /*
254                  *      FIXME: HOME SERVER
255                  *
256                  *      What the heck is this code doing, and why?
257                  */
258
259                 if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
260                     vp->lvalue == realm->home_auth->ipaddr.ipaddr.ip4addr.s_addr) {
261                         DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
262                         return 0;
263                 }
264                 if (request->packet->code == PW_ACCOUNTING_REQUEST &&
265                     vp->lvalue == realm->home_acct->ipaddr.ipaddr.ip4addr.s_addr) {
266                         DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
267                         return 0;
268                 }
269 #endif
270         }
271
272         /*
273          *      We got this far, which means we have a realm, set returnrealm
274          */
275         *returnrealm = realm;
276         return 0;
277 }
278
279 /*
280  *      Add a "Proxy-To-Realm" attribute to the request.
281  */
282 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
283 {
284         VALUE_PAIR *vp;
285
286         /*
287          *      Tell the server to proxy this request to another
288          *      realm.
289          */
290         vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
291         if (!vp) {
292                 radlog(L_ERR|L_CONS, "no memory");
293                 exit(1);
294         }
295
296         /*
297          *  Add it, even if it's already present.
298          */
299         pairadd(vps, vp);
300 }
301
302 /*
303  *  Perform the realm module instantiation.  Configuration info is
304  *  stored in *instance for later use.
305  */
306
307 static int realm_instantiate(CONF_SECTION *conf, void **instance)
308 {
309         struct realm_config_t *inst;
310
311         /* setup a storage area for instance data */
312         inst = rad_malloc(sizeof(*inst));
313         if (!inst) {
314                 return -1;
315         }
316         memset(inst, 0, sizeof(*inst));
317
318         if(cf_section_parse(conf, inst, module_config) < 0) {
319                free(inst);
320                return -1;
321         }
322
323         if(strcasecmp(inst->formatstring, "suffix") == 0) {
324              inst->format = REALM_FORMAT_SUFFIX;
325         } else if(strcasecmp(inst->formatstring, "prefix") == 0) {
326              inst->format = REALM_FORMAT_PREFIX;
327         } else {
328              radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
329              free(inst);
330              return -1;
331         }
332         if(strlen(inst->delim) != 1) {
333              radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
334              free(inst);
335              return -1;
336         }
337
338         *instance = inst;
339         return 0;
340
341 }
342
343
344
345
346
347 /*
348  *  Examine a request for a username with an realm, and if it
349  *  corresponds to something in the realms file, set that realm as
350  *  Proxy-To.
351  *
352  *  This should very nearly duplicate the old proxy_send() code
353  */
354 static int realm_authorize(void *instance, REQUEST *request)
355 {
356         REALM *realm;
357
358         /*
359          *      Check if we've got to proxy the request.
360          *      If not, return without adding a Proxy-To-Realm
361          *      attribute.
362          */
363         if (check_for_realm(instance, request, &realm) < 0) {
364                 return RLM_MODULE_FAIL;
365         }
366         if (!realm) {
367                 return RLM_MODULE_NOOP;
368         }
369
370         /*
371          *      Maybe add a Proxy-To-Realm attribute to the request.
372          */
373         DEBUG2("    rlm_realm: Preparing to proxy authentication request to realm \"%s\"\n",
374                realm->name);
375         add_proxy_to_realm(&request->config_items, realm);
376
377         return RLM_MODULE_UPDATED; /* try the next module */
378 }
379
380 /*
381  * This does the exact same thing as the realm_authorize, it's just called
382  * differently.
383  */
384 static int realm_preacct(void *instance, REQUEST *request)
385 {
386         const char *name = (char *)request->username->vp_strvalue;
387         REALM *realm;
388
389         if (!name)
390           return RLM_MODULE_OK;
391
392
393         /*
394          *      Check if we've got to proxy the request.
395          *      If not, return without adding a Proxy-To-Realm
396          *      attribute.
397          */
398         if (check_for_realm(instance, request, &realm) < 0) {
399                 return RLM_MODULE_FAIL;
400         }
401         if (!realm) {
402                 return RLM_MODULE_NOOP;
403         }
404
405
406         /*
407          *      Maybe add a Proxy-To-Realm attribute to the request.
408          */
409         DEBUG2("    rlm_realm: Preparing to proxy accounting request to realm \"%s\"\n",
410                realm->name);
411         add_proxy_to_realm(&request->config_items, realm);
412
413         return RLM_MODULE_UPDATED; /* try the next module */
414 }
415
416 static int realm_detach(void *instance)
417 {
418         struct realm_config_t *inst = instance;
419         free(instance);
420         return 0;
421 }
422
423 /* globally exported name */
424 module_t rlm_realm = {
425         RLM_MODULE_INIT,
426         "realm",
427         0,                              /* type: reserved */
428         realm_instantiate,              /* instantiation */
429         realm_detach,                   /* detach */
430         {
431                 NULL,                   /* authentication */
432                 realm_authorize,        /* authorization */
433                 realm_preacct,  /* preaccounting */
434                 NULL,                   /* accounting */
435                 NULL,                   /* checksimul */
436                 NULL,                   /* pre-proxy */
437                 NULL,                   /* post-proxy */
438                 NULL                    /* post-auth */
439         },
440 };