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