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