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.
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.
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
19 * @brief Parses NAIs and assigns requests to realms.
21 * @copyright 2000-2013 The FreeRADIUS server project
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
29 #define REALM_FORMAT_PREFIX 0
30 #define REALM_FORMAT_SUFFIX 1
32 typedef struct realm_config_t {
40 static CONF_PARSER module_config[] = {
41 { "format", PW_TYPE_STRING_PTR,
42 offsetof(realm_config_t,formatstring), NULL, "suffix" },
43 { "delimiter", PW_TYPE_STRING_PTR,
44 offsetof(realm_config_t,delim), NULL, "@" },
45 { "ignore_default", PW_TYPE_BOOLEAN,
46 offsetof(realm_config_t,ignore_default), NULL, "no" },
47 { "ignore_null", PW_TYPE_BOOLEAN,
48 offsetof(realm_config_t,ignore_null), NULL, "no" },
49 { NULL, -1, 0, NULL, NULL } /* end the list */
53 * Internal function to cut down on duplicated code.
55 * Returns -1 on failure, 0 on no failure. returnrealm
56 * is NULL on don't proxy, realm otherwise.
58 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
60 char namebuf[MAX_STRING_LEN];
62 const char *realmname = NULL;
67 struct realm_config_t *inst = instance;
69 /* initiate returnrealm */
73 * If the request has a proxy entry, then it's a proxy
74 * reply, and we're walking through the module list again.
76 * In that case, don't bother trying to proxy the request
79 * Also, if there's no User-Name attribute, we can't
82 if ((request->username == NULL)
84 || (request->proxy != NULL)
88 RDEBUG2("Proxy reply, or no User-Name. Ignoring.");
93 * Check for 'Realm' attribute. If it exists, then we've proxied
94 * it already ( via another rlm_realm instance ) and should return.
97 if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
98 RDEBUG2("Request already proxied. Ignoring.");
103 * We will be modifing this later, so we want our own copy
106 strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
112 case REALM_FORMAT_SUFFIX:
114 /* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
115 ptr = strrchr(username, inst->delim[0]);
122 case REALM_FORMAT_PREFIX:
124 /* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
126 ptr = strchr(username, inst->delim[0]);
130 realmname = username;
141 * Print out excruciatingly descriptive debugging messages
142 * for the people who find it too difficult to think about
146 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
147 realmname, request->username->vp_strvalue);
149 if( inst->ignore_null ) {
150 RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
151 inst->delim[0], request->username->vp_strvalue);
152 return RLM_MODULE_NOOP;
154 RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
155 inst->delim[0], request->username->vp_strvalue);
159 * Allow DEFAULT realms unless told not to.
161 realm = realm_find(realmname);
163 RDEBUG2("No such realm \"%s\"",
164 (realmname == NULL) ? "NULL" : realmname);
165 return RLM_MODULE_NOOP;
167 if( inst->ignore_default &&
168 (strcmp(realm->name, "DEFAULT")) == 0) {
169 RDEBUG2("Found DEFAULT, but skipping due to config.");
170 return RLM_MODULE_NOOP;
173 RDEBUG2("Found realm \"%s\"", realm->name);
176 * If we've been told to strip the realm off, then do so.
178 if (realm->striprealm) {
180 * Create the Stripped-User-Name attribute, if it
184 if (request->username->da->attr != PW_STRIPPED_USER_NAME) {
185 vp = radius_paircreate(request, &request->packet->vps,
186 PW_STRIPPED_USER_NAME, 0);
187 RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
189 vp = request->username;
190 RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
193 strlcpy(vp->vp_strvalue, username, sizeof(vp->vp_strvalue));
194 vp->length = strlen((char *)vp->vp_strvalue);
195 request->username = vp;
199 * Add the realm name to the request.
200 * If the realm is a regex, the use the realm as entered
201 * by the user. Otherwise, use the configured realm name,
202 * as realm name comparison is case insensitive. We want
203 * to use the configured name, rather than what the user
206 if (realm->name[0] != '~') realmname = realm->name;
207 pairadd(&request->packet->vps, pairmake("Realm", realmname,
209 RDEBUG2("Adding Realm = \"%s\"", realmname);
212 * Figure out what to do with the request.
214 switch (request->packet->code) {
216 RDEBUG2("Unknown packet code %d\n",
217 request->packet->code);
218 return RLM_MODULE_OK; /* don't do anything */
221 * Perhaps accounting proxying was turned off.
223 case PW_ACCOUNTING_REQUEST:
224 if (!realm->acct_pool) {
225 RDEBUG2("Accounting realm is LOCAL.");
226 return RLM_MODULE_OK;
231 * Perhaps authentication proxying was turned off.
233 case PW_AUTHENTICATION_REQUEST:
234 if (!realm->auth_pool) {
235 RDEBUG2("Authentication realm is LOCAL.");
236 return RLM_MODULE_OK;
242 RDEBUG2("Proxying request from user %s to realm %s",
243 username, realm->name);
246 * Skip additional checks if it's not an accounting
249 if (request->packet->code != PW_ACCOUNTING_REQUEST) {
250 *returnrealm = realm;
251 return RLM_MODULE_UPDATED;
255 * FIXME: Each server should have a unique server key,
256 * and put it in the accounting packet. Every server
257 * should know about the keys, and NOT proxy requests to
258 * a server with key X if the packet already contains key
263 * If this request has arrived from another freeradius server
264 * that has already proxied the request, we don't need to do
267 vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
268 if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
270 fr_ipaddr_t my_ipaddr;
272 my_ipaddr.af = AF_INET;
273 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
276 * Loop over the home accounting servers for this
277 * realm. If one of them has the same IP as the
278 * FreeRADIUS-Proxied-To attribute, then the
279 * packet has already been sent there. Don't
280 * send it there again.
282 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
283 if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
285 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
286 return RLM_MODULE_OK;
291 * See detail_recv() in src/main/listen.c for the
295 } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
296 !fr_inaddr_any(&request->packet->src_ipaddr)) {
300 * Loop over the home accounting servers for this
301 * realm. If one of them has the same IP as the
302 * FreeRADIUS-Proxied-To attribute, then the
303 * packet has already been sent there. Don't
304 * send it there again.
306 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
307 if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
308 &request->packet->src_ipaddr) == 0) &&
309 (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
310 RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
311 return RLM_MODULE_OK;
314 #endif /* WITH_DETAIL */
316 #endif /* WITH_PROXY */
319 * We got this far, which means we have a realm, set returnrealm
321 *returnrealm = realm;
323 return RLM_MODULE_UPDATED;
327 * Add a "Proxy-To-Realm" attribute to the request.
329 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
334 * Tell the server to proxy this request to another
337 vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
339 radlog(L_ERR, "no memory");
344 * Add it, even if it's already present.
350 * Perform the realm module instantiation. Configuration info is
351 * stored in *instance for later use.
354 static int realm_instantiate(CONF_SECTION *conf, void **instance)
356 struct realm_config_t *inst;
358 /* setup a storage area for instance data */
359 *instance = inst = talloc_zero(conf, struct realm_config_t);
360 if (!inst) return -1;
362 if(cf_section_parse(conf, inst, module_config) < 0) {
366 if(strcasecmp(inst->formatstring, "suffix") == 0) {
367 inst->format = REALM_FORMAT_SUFFIX;
369 } else if(strcasecmp(inst->formatstring, "prefix") == 0) {
370 inst->format = REALM_FORMAT_PREFIX;
373 radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
376 if(strlen(inst->delim) != 1) {
377 radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
386 * Examine a request for a username with an realm, and if it
387 * corresponds to something in the realms file, set that realm as
390 * This should very nearly duplicate the old proxy_send() code
392 static rlm_rcode_t realm_authorize(void *instance, REQUEST *request)
398 * Check if we've got to proxy the request.
399 * If not, return without adding a Proxy-To-Realm
402 rcode = check_for_realm(instance, request, &realm);
403 if (rcode != RLM_MODULE_UPDATED) return rcode;
404 if (!realm) return RLM_MODULE_NOOP;
407 * Maybe add a Proxy-To-Realm attribute to the request.
409 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
411 add_proxy_to_realm(&request->config_items, realm);
413 return RLM_MODULE_UPDATED; /* try the next module */
417 * This does the exact same thing as the realm_authorize, it's just called
420 static rlm_rcode_t realm_preacct(void *instance, REQUEST *request)
423 const char *name = (char *)request->username->vp_strvalue;
427 return RLM_MODULE_OK;
431 * Check if we've got to proxy the request.
432 * If not, return without adding a Proxy-To-Realm
435 rcode = check_for_realm(instance, request, &realm);
436 if (rcode != RLM_MODULE_UPDATED) return rcode;
437 if (!realm) return RLM_MODULE_NOOP;
440 * Maybe add a Proxy-To-Realm attribute to the request.
442 RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
444 add_proxy_to_realm(&request->config_items, realm);
446 return RLM_MODULE_UPDATED; /* try the next module */
451 * CoA realms via Operator-Name. Because the realm isn't in a
452 * User-Name, concepts like "prefix" and "suffix' don't matter.
454 static rlm_rcode_t realm_coa(UNUSED void *instance, REQUEST *request)
459 if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
460 RDEBUG2("Request already proxied. Ignoring.");
461 return RLM_MODULE_OK;
464 vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
465 if (!vp) return RLM_MODULE_NOOP;
468 * Catch the case of broken dictionaries.
470 if (vp->da->type != PW_TYPE_STRING) return RLM_MODULE_NOOP;
473 * The string is too short.
475 if (vp->length == 1) return RLM_MODULE_NOOP;
478 * '1' means "the rest of the string is a realm"
480 if (vp->vp_strvalue[0] != '1') return RLM_MODULE_NOOP;
482 realm = realm_find(vp->vp_strvalue + 1);
483 if (!realm) return RLM_MODULE_NOTFOUND;
485 if (!realm->coa_pool) {
486 RDEBUG2("CoA realm is LOCAL.");
487 return RLM_MODULE_OK;
491 * Maybe add a Proxy-To-Realm attribute to the request.
493 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
495 add_proxy_to_realm(&request->config_items, realm);
497 return RLM_MODULE_UPDATED; /* try the next module */
501 /* globally exported name */
502 module_t rlm_realm = {
505 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
506 realm_instantiate, /* instantiation */
509 NULL, /* authentication */
510 realm_authorize, /* authorization */
511 realm_preacct, /* preaccounting */
512 NULL, /* accounting */
513 NULL, /* checksimul */
514 NULL, /* pre-proxy */
515 NULL, /* post-proxy */
518 , realm_coa, /* recv-coa */