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