Package files in 3.0.1 not in 3.0.3 and chbind site
[freeradius.git] / src / modules / rlm_realm / rlm_realm.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_realm.c
19  * @brief Parses NAIs and assigns requests to realms.
20  *
21  * @copyright 2000-2013  The FreeRADIUS server project
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27
28 #include "trustrouter_integ.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   bool          ignore_default;
38   bool          ignore_null;
39   char          *default_community;
40   char          *rp_realm;
41   char          *trust_router;
42   unsigned int  tr_port;
43 } realm_config_t;
44
45 #define stringify(s) #s
46
47 static CONF_PARSER module_config[] = {
48   { "format", PW_TYPE_STRING_PTR,
49     offsetof(realm_config_t,formatstring), NULL, "suffix" },
50   { "delimiter", PW_TYPE_STRING_PTR,
51     offsetof(realm_config_t,delim), NULL, "@" },
52   { "ignore_default", PW_TYPE_BOOLEAN,
53     offsetof(realm_config_t,ignore_default), NULL, "no" },
54   { "ignore_null", PW_TYPE_BOOLEAN,
55     offsetof(realm_config_t,ignore_null), NULL, "no" },
56   { "default_community", PW_TYPE_STRING_PTR,
57     offsetof(realm_config_t,default_community), NULL, "none" },
58   { "rp_realm", PW_TYPE_STRING_PTR,
59     offsetof(realm_config_t,rp_realm), NULL, "none" },
60   { "trust_router", PW_TYPE_STRING_PTR,
61     offsetof(realm_config_t,trust_router), NULL, "none" },
62   { "tr_port", PW_TYPE_INTEGER,
63     offsetof(realm_config_t,tr_port), NULL, "0" },
64   //    offsetof(realm_config_t,tr_port), NULL, (stringify(TID_PORT)) },
65   { NULL, -1, 0, NULL, NULL }    /* end the list */
66 };
67
68 /*
69  *      Internal function to cut down on duplicated code.
70  *
71  *      Returns -1 on failure, 0 on no failure.  returnrealm
72  *      is NULL on don't proxy, realm otherwise.
73  */
74 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
75 {
76         char *namebuf;
77         char *username;
78         char const *realmname = NULL;
79         char *ptr;
80         VALUE_PAIR *vp;
81         REALM *realm;
82
83         struct realm_config_t *inst = instance;
84
85         /* initiate returnrealm */
86         *returnrealm = NULL;
87
88         /*
89          *      If the request has a proxy entry, then it's a proxy
90          *      reply, and we're walking through the module list again.
91          *
92          *      In that case, don't bother trying to proxy the request
93          *      again.
94          *
95          *      Also, if there's no User-Name attribute, we can't
96          *      proxy it, either.
97          */
98         if ((!request->username)
99 #ifdef WITH_PROXY
100             || (request->proxy != NULL)
101 #endif
102             ) {
103
104                 RDEBUG2("Proxy reply, or no User-Name.  Ignoring.");
105                 return RLM_MODULE_OK;
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 (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
114                 RDEBUG2("Request already has destination realm set.  Ignoring.");
115                 return RLM_MODULE_OK;
116         }
117
118         /*
119          *      We will be modifing this later, so we want our own copy
120          *      of it.
121          */
122         namebuf = talloc_strdup(request,  request->username->vp_strvalue);
123         username = namebuf;
124
125         switch(inst->format) {
126         case REALM_FORMAT_SUFFIX:
127
128           /* DEBUG2("  rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
129                 ptr = strrchr(username, inst->delim[0]);
130                 if (ptr) {
131                         *ptr = '\0';
132                         realmname = ptr + 1;
133                 }
134                 break;
135
136         case REALM_FORMAT_PREFIX:
137
138                 /* DEBUG2("  rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
139
140                 ptr = strchr(username, inst->delim[0]);
141                 if (ptr) {
142                         *ptr = '\0';
143                      ptr++;
144                      realmname = username;
145                      username = ptr;
146                 }
147                 break;
148
149         default:
150                 realmname = NULL;
151                 break;
152         }
153
154         /*
155          *      Print out excruciatingly descriptive debugging messages
156          *      for the people who find it too difficult to think about
157          *      what's going on.
158          */
159         if (realmname) {
160                 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
161                        realmname, request->username->vp_strvalue);
162         } else {
163                 if (inst->ignore_null ) {
164                         RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
165                         inst->delim[0], request->username->vp_strvalue);
166                         talloc_free(namebuf);
167                         return RLM_MODULE_NOOP;
168                 }
169                 RDEBUG2("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);
177         if (!realm) 
178           realm = tr_query_realm(realmname, inst->default_community, inst->rp_realm, inst->trust_router, inst->tr_port);
179         if (!realm) {
180                 RDEBUG2("No such realm \"%s\"",
181                         (!realmname) ? "NULL" : realmname);
182                 talloc_free(namebuf);
183                 return RLM_MODULE_NOOP;
184         }
185         if( inst->ignore_default &&
186             (strcmp(realm->name, "DEFAULT")) == 0) {
187                 RDEBUG2("Found DEFAULT, but skipping due to config.");
188                 talloc_free(namebuf);
189                 return RLM_MODULE_NOOP;
190         }
191
192         RDEBUG2("Found realm \"%s\"", realm->name);
193
194         /*
195          *      If we've been told to strip the realm off, then do so.
196          */
197         if (realm->striprealm) {
198                 /*
199                  *      Create the Stripped-User-Name attribute, if it
200                  *      doesn't exist.
201                  *
202                  */
203                 if (request->username->da->attr != PW_STRIPPED_USER_NAME) {
204                         vp = radius_paircreate(request, &request->packet->vps,
205                                                PW_STRIPPED_USER_NAME, 0);
206                         RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
207                 } else {
208                         vp = request->username;
209                         RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
210                 }
211
212                 pairstrcpy(vp, username);
213                 request->username = vp;
214         }
215
216         /*
217          *      Add the realm name to the request.
218          *      If the realm is a regex, the use the realm as entered
219          *      by the user.  Otherwise, use the configured realm name,
220          *      as realm name comparison is case insensitive.  We want
221          *      to use the configured name, rather than what the user
222          *      entered.
223          */
224         if (realm->name[0] != '~') realmname = realm->name;
225         pairmake_packet("Realm", realmname, T_OP_EQ);
226         RDEBUG2("Adding Realm = \"%s\"", realmname);
227
228         talloc_free(namebuf);
229         realmname = username = NULL;
230
231         /*
232          *      Figure out what to do with the request.
233          */
234         switch (request->packet->code) {
235         default:
236                 RDEBUG2("Unknown packet code %d\n",
237                        request->packet->code);
238                 return RLM_MODULE_OK;           /* don't do anything */
239
240                 /*
241                  *      Perhaps accounting proxying was turned off.
242                  */
243         case PW_ACCOUNTING_REQUEST:
244                 if (!realm->acct_pool) {
245                         RDEBUG2("Accounting realm is LOCAL.");
246                         return RLM_MODULE_OK;
247                 }
248                 break;
249
250                 /*
251                  *      Perhaps authentication proxying was turned off.
252                  */
253         case PW_AUTHENTICATION_REQUEST:
254                 if (!realm->auth_pool) {
255                         RDEBUG2("Authentication realm is LOCAL.");
256                         return RLM_MODULE_OK;
257                 }
258                 break;
259         }
260
261 #ifdef WITH_PROXY
262         RDEBUG2("Proxying request from user %s to realm %s",
263                request->username->vp_strvalue, realm->name);
264
265         /*
266          *      Skip additional checks if it's not an accounting
267          *      request.
268          */
269         if (request->packet->code != PW_ACCOUNTING_REQUEST) {
270                 *returnrealm = realm;
271                 return RLM_MODULE_UPDATED;
272         }
273
274         /*
275          *      FIXME: Each server should have a unique server key,
276          *      and put it in the accounting packet.  Every server
277          *      should know about the keys, and NOT proxy requests to
278          *      a server with key X if the packet already contains key
279          *      X.
280          */
281
282         /*
283          *      If this request has arrived from another freeradius server
284          *      that has already proxied the request, we don't need to do
285          *      it again.
286          */
287         vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
288         if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
289                 int i;
290                 fr_ipaddr_t my_ipaddr;
291
292                 my_ipaddr.af = AF_INET;
293                 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
294
295                 /*
296                  *      Loop over the home accounting servers for this
297                  *      realm.  If one of them has the same IP as the
298                  *      FreeRADIUS-Proxied-To attribute, then the
299                  *      packet has already been sent there.  Don't
300                  *      send it there again.
301                  */
302                 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
303                         if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
304                                             &my_ipaddr) == 0) {
305                                 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
306                                 return RLM_MODULE_OK;
307                         }
308                 }
309
310                 /*
311                  *      See detail_recv() in src/main/listen.c for the
312                  *      additional checks.
313                  */
314 #ifdef WITH_DETAIL
315         } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
316                    !fr_inaddr_any(&request->packet->src_ipaddr)) {
317                 int i;
318
319                 /*
320                  *      Loop over the home accounting servers for this
321                  *      realm.  If one of them has the same IP as the
322                  *      FreeRADIUS-Proxied-To attribute, then the
323                  *      packet has already been sent there.  Don't
324                  *      send it there again.
325                  */
326                 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
327                         if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
328                                              &request->packet->src_ipaddr) == 0) &&
329                             (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
330                                 RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
331                                 return RLM_MODULE_OK;
332                         }
333                 }
334 #endif  /* WITH_DETAIL */
335         }
336 #endif  /* WITH_PROXY */
337
338         /*
339          *      We got this far, which means we have a realm, set returnrealm
340          */
341         *returnrealm = realm;
342
343         return RLM_MODULE_UPDATED;
344 }
345
346 /*
347  *  Perform the realm module instantiation.  Configuration info is
348  *  stored in *instance for later use.
349  */
350
351 static int mod_instantiate(CONF_SECTION *conf, void *instance)
352 {
353         struct realm_config_t *inst = instance;
354
355         /* initialize the trust router integration code */
356         if (tr_init() < 0)
357                return -1;
358         if (strcasecmp(inst->formatstring, "suffix") == 0) {
359
360              inst->format = REALM_FORMAT_SUFFIX;
361
362         } else if (strcasecmp(inst->formatstring, "prefix") == 0) {
363              inst->format = REALM_FORMAT_PREFIX;
364
365         } else {
366                 cf_log_err_cs(conf, "Invalid value \"%s\" for format",
367                               inst->formatstring);
368              return -1;
369         }
370
371         if (strlen(inst->delim) != 1) {
372                 cf_log_err_cs(conf, "Invalid value \"%s\" for delimiter",
373                               inst->delim);
374              return -1;
375         }
376
377         return 0;
378 }
379
380
381 /*
382  *  Examine a request for a username with an realm, and if it
383  *  corresponds to something in the realms file, set that realm as
384  *  Proxy-To.
385  *
386  *  This should very nearly duplicate the old proxy_send() code
387  */
388 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
389 {
390         rlm_rcode_t rcode;
391         REALM *realm;
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         rcode = check_for_realm(instance, request, &realm);
399         if (rcode != RLM_MODULE_UPDATED) return rcode;
400         if (!realm) return RLM_MODULE_NOOP;
401
402         /*
403          *      Maybe add a Proxy-To-Realm attribute to the request.
404          */
405         RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
406                realm->name);
407         pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
408
409         return RLM_MODULE_UPDATED; /* try the next module */
410 }
411
412 /*
413  * This does the exact same thing as the mod_authorize, it's just called
414  * differently.
415  */
416 static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
417 {
418         int rcode;
419         REALM *realm;
420
421         if (!request->username) {
422                 return RLM_MODULE_NOOP;
423         }
424
425         /*
426          *      Check if we've got to proxy the request.
427          *      If not, return without adding a Proxy-To-Realm
428          *      attribute.
429          */
430         rcode = check_for_realm(instance, request, &realm);
431         if (rcode != RLM_MODULE_UPDATED) return rcode;
432         if (!realm) return RLM_MODULE_NOOP;
433
434         /*
435          *      Maybe add a Proxy-To-Realm attribute to the request.
436          */
437         RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
438                realm->name);
439         pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
440
441         return RLM_MODULE_UPDATED; /* try the next module */
442 }
443
444 #ifdef WITH_COA
445 /*
446  *      CoA realms via Operator-Name.  Because the realm isn't in a
447  *      User-Name, concepts like "prefix" and "suffix' don't matter.
448  */
449 static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
450 {
451         VALUE_PAIR *vp;
452         REALM *realm;
453
454         if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
455                 RDEBUG2("Request already has destination realm set.  Ignoring.");
456                 return RLM_MODULE_OK;
457         }
458
459         vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
460         if (!vp) return RLM_MODULE_NOOP;
461
462         /*
463          *      Catch the case of broken dictionaries.
464          */
465         if (vp->da->type != PW_TYPE_STRING) return RLM_MODULE_NOOP;
466
467         /*
468          *      The string is too short.
469          */
470         if (vp->length == 1) return RLM_MODULE_NOOP;
471
472         /*
473          *      '1' means "the rest of the string is a realm"
474          */
475         if (vp->vp_strvalue[0] != '1') return RLM_MODULE_NOOP;
476
477         realm = realm_find(vp->vp_strvalue + 1);
478         if (!realm) return RLM_MODULE_NOTFOUND;
479
480
481         if (!realm->coa_pool) {
482                 RDEBUG2("CoA realm is LOCAL.");
483                 return RLM_MODULE_OK;
484         }
485
486         /*
487          *      Maybe add a Proxy-To-Realm attribute to the request.
488          */
489         RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
490                realm->name);
491         pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
492
493         return RLM_MODULE_UPDATED; /* try the next module */
494 }
495 #endif
496
497 /* globally exported name */
498 module_t rlm_realm = {
499         RLM_MODULE_INIT,
500         "realm",
501         RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
502         sizeof(struct realm_config_t),
503         module_config,
504         mod_instantiate,                /* instantiation */
505         NULL,                           /* detach */
506         {
507                 NULL,                   /* authentication */
508                 mod_authorize,  /* authorization */
509                 mod_preacct,            /* preaccounting */
510                 NULL,                   /* accounting */
511                 NULL,                   /* checksimul */
512                 NULL,                   /* pre-proxy */
513                 NULL,                   /* post-proxy */
514                 NULL                    /* post-auth */
515 #ifdef WITH_COA
516                 , realm_recv_coa,       /* recv-coa */
517                 NULL                    /* send-coa */
518 #endif
519         },
520 };