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.
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.
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
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * FIXME add copyrights
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
30 #define REALM_FORMAT_PREFIX 0
31 #define REALM_FORMAT_SUFFIX 1
33 typedef struct realm_config_t {
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 */
54 * Internal function to cut down on duplicated code.
56 * Returns -1 on failure, 0 on no failure. returnrealm
57 * is NULL on don't proxy, realm otherwise.
59 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
61 char namebuf[MAX_STRING_LEN];
63 char *realmname = NULL;
68 struct realm_config_t *inst = instance;
70 /* initiate returnrealm */
74 * If the request has a proxy entry, then it's a proxy
75 * reply, and we're walking through the module list again.
77 * In that case, don't bother trying to proxy the request
80 * Also, if there's no User-Name attribute, we can't
83 if ((request->proxy != NULL) ||
84 (request->username == NULL)) {
85 RDEBUG2("Proxy reply, or no User-Name. Ignoring.");
90 * Check for 'Realm' attribute. If it exists, then we've proxied
91 * it already ( via another rlm_realm instance ) and should return.
94 if (pairfind(request->packet->vps, PW_REALM, 0) != NULL ) {
95 RDEBUG2("Request already proxied. Ignoring.");
100 * We will be modifing this later, so we want our own copy
103 strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
109 case REALM_FORMAT_SUFFIX:
111 /* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
112 realmname = strrchr(username, inst->delim[0]);
119 case REALM_FORMAT_PREFIX:
121 /* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
123 ptr = strchr(username, inst->delim[0]);
127 realmname = username;
138 * Print out excruciatingly descriptive debugging messages
139 * for the people who find it too difficult to think about
143 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
144 realmname, request->username->vp_strvalue);
146 if( inst->ignore_null ) {
147 RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
148 inst->delim[0], request->username->vp_strvalue);
149 return RLM_MODULE_NOOP;
151 RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
152 inst->delim[0], request->username->vp_strvalue);
156 * Allow DEFAULT realms unless told not to.
158 realm = realm_find(realmname);
160 RDEBUG2("No such realm \"%s\"",
161 (realmname == NULL) ? "NULL" : realmname);
162 return RLM_MODULE_NOOP;
164 if( inst->ignore_default &&
165 (strcmp(realm->name, "DEFAULT")) == 0) {
166 RDEBUG2("Found DEFAULT, but skipping due to config.");
167 return RLM_MODULE_NOOP;
170 RDEBUG2("Found realm \"%s\"", realm->name);
173 * If we've been told to strip the realm off, then do so.
175 if (realm->striprealm) {
177 * Create the Stripped-User-Name attribute, if it
181 if (request->username->attribute != PW_STRIPPED_USER_NAME) {
182 vp = radius_paircreate(request, &request->packet->vps,
183 PW_STRIPPED_USER_NAME,
185 RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
187 vp = request->username;
188 RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
191 strcpy(vp->vp_strvalue, username);
192 vp->length = strlen((char *)vp->vp_strvalue);
193 request->username = vp;
197 * Add the realm name to the request.
198 * If the realm is a regex, the use the realm as entered
199 * by the user. Otherwise, use the configured realm name,
200 * as realm name comparison is case insensitive. We want
201 * to use the configured name, rather than what the user
204 if (realm->name[0] != '~') realmname = realm->name;
205 pairadd(&request->packet->vps, pairmake("Realm", realmname,
207 RDEBUG2("Adding Realm = \"%s\"", realmname);
210 * Figure out what to do with the request.
212 switch (request->packet->code) {
214 RDEBUG2("Unknown packet code %d\n",
215 request->packet->code);
216 return RLM_MODULE_OK; /* don't do anything */
219 * Perhaps accounting proxying was turned off.
221 case PW_ACCOUNTING_REQUEST:
222 if (!realm->acct_pool) {
223 RDEBUG2("Accounting realm is LOCAL.");
224 return RLM_MODULE_OK;
229 * Perhaps authentication proxying was turned off.
231 case PW_AUTHENTICATION_REQUEST:
232 if (!realm->auth_pool) {
233 RDEBUG2("Authentication realm is LOCAL.");
234 return RLM_MODULE_OK;
239 RDEBUG2("Proxying request from user %s to realm %s",
240 username, realm->name);
243 * Skip additional checks if it's not an accounting
246 if (request->packet->code != PW_ACCOUNTING_REQUEST) {
247 *returnrealm = realm;
248 return RLM_MODULE_UPDATED;
252 * FIXME: Each server should have a unique server key,
253 * and put it in the accounting packet. Every server
254 * should know about the keys, and NOT proxy requests to
255 * a server with key X if the packet already contains key
260 * If this request has arrived from another freeradius server
261 * that has already proxied the request, we don't need to do
264 vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0);
265 if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
267 fr_ipaddr_t my_ipaddr;
269 my_ipaddr.af = AF_INET;
270 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
273 * Loop over the home accounting servers for this
274 * realm. If one of them has the same IP as the
275 * FreeRADIUS-Proxied-To attribute, then the
276 * packet has already been sent there. Don't
277 * send it there again.
279 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
280 if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
282 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
283 return RLM_MODULE_OK;
288 * See detail_recv() in src/main/listen.c for the
291 } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
292 ((request->packet->src_ipaddr.af == AF_INET6) ||
293 (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)))) {
297 * Loop over the home accounting servers for this
298 * realm. If one of them has the same IP as the
299 * FreeRADIUS-Proxied-To attribute, then the
300 * packet has already been sent there. Don't
301 * send it there again.
303 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
304 if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
305 &request->packet->src_ipaddr) == 0) &&
306 (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
307 RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
308 return RLM_MODULE_OK;
315 * We got this far, which means we have a realm, set returnrealm
317 *returnrealm = realm;
318 return RLM_MODULE_UPDATED;
322 * Add a "Proxy-To-Realm" attribute to the request.
324 static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
329 * Tell the server to proxy this request to another
332 vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
334 radlog(L_ERR|L_CONS, "no memory");
339 * Add it, even if it's already present.
345 * Perform the realm module instantiation. Configuration info is
346 * stored in *instance for later use.
349 static int realm_instantiate(CONF_SECTION *conf, void **instance)
351 struct realm_config_t *inst;
353 /* setup a storage area for instance data */
354 inst = rad_malloc(sizeof(*inst));
358 memset(inst, 0, sizeof(*inst));
360 if(cf_section_parse(conf, inst, module_config) < 0) {
365 if(strcasecmp(inst->formatstring, "suffix") == 0) {
366 inst->format = REALM_FORMAT_SUFFIX;
367 } else if(strcasecmp(inst->formatstring, "prefix") == 0) {
368 inst->format = REALM_FORMAT_PREFIX;
370 radlog(L_ERR, "Bad value \"%s\" for realm format value", inst->formatstring);
374 if(strlen(inst->delim) != 1) {
375 radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
390 * Examine a request for a username with an realm, and if it
391 * corresponds to something in the realms file, set that realm as
394 * This should very nearly duplicate the old proxy_send() code
396 static int realm_authorize(void *instance, REQUEST *request)
402 * Check if we've got to proxy the request.
403 * If not, return without adding a Proxy-To-Realm
406 rcode = check_for_realm(instance, request, &realm);
407 if (rcode != RLM_MODULE_UPDATED) return rcode;
408 if (!realm) return RLM_MODULE_NOOP;
411 * Maybe add a Proxy-To-Realm attribute to the request.
413 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
415 add_proxy_to_realm(&request->config_items, realm);
417 return RLM_MODULE_UPDATED; /* try the next module */
421 * This does the exact same thing as the realm_authorize, it's just called
424 static int realm_preacct(void *instance, REQUEST *request)
427 const char *name = (char *)request->username->vp_strvalue;
431 return RLM_MODULE_OK;
435 * Check if we've got to proxy the request.
436 * If not, return without adding a Proxy-To-Realm
439 rcode = check_for_realm(instance, request, &realm);
440 if (rcode != RLM_MODULE_UPDATED) return rcode;
441 if (!realm) return RLM_MODULE_NOOP;
444 * Maybe add a Proxy-To-Realm attribute to the request.
446 RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
448 add_proxy_to_realm(&request->config_items, realm);
450 return RLM_MODULE_UPDATED; /* try the next module */
453 static int realm_detach(void *instance)
459 /* globally exported name */
460 module_t rlm_realm = {
463 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
464 realm_instantiate, /* instantiation */
465 realm_detach, /* detach */
467 NULL, /* authentication */
468 realm_authorize, /* authorization */
469 realm_preacct, /* preaccounting */
470 NULL, /* accounting */
471 NULL, /* checksimul */
472 NULL, /* pre-proxy */
473 NULL, /* post-proxy */