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 as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief Parses NAIs and assigns requests to realms.
22 * @copyright 2000-2013 The FreeRADIUS server project
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
29 #include "trustrouter.h"
31 #define REALM_FORMAT_PREFIX 0
32 #define REALM_FORMAT_SUFFIX 1
34 typedef struct rlm_realm_t {
36 char const *format_string;
41 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
42 char const *default_community;
44 char const *trust_router;
49 static CONF_PARSER module_config[] = {
50 { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, format_string), "suffix" },
51 { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, delim), "@" },
52 { "ignore_default", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_default), "no" },
53 { "ignore_null", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_null), "no" },
54 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
55 { "default_community", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,default_community), "none" },
56 { "rp_realm", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,rp_realm), "none" },
57 { "trust_router", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,trust_router), "none" },
58 { "tr_port", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_realm_t,tr_port), "0" },
60 CONF_PARSER_TERMINATOR
64 * Internal function to cut down on duplicated code.
66 * Returns -1 on failure, 0 on no failure. returnrealm
67 * is NULL on don't proxy, realm otherwise.
69 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
73 char const *realmname = NULL;
78 struct rlm_realm_t *inst = instance;
80 /* initiate returnrealm */
84 * If the request has a proxy entry, then it's a proxy
85 * reply, and we're walking through the module list again.
87 * In that case, don't bother trying to proxy the request
90 * Also, if there's no User-Name attribute, we can't
93 if ((!request->username)
95 || (request->proxy != NULL)
99 RDEBUG2("Proxy reply, or no User-Name. Ignoring");
100 return RLM_MODULE_NOOP;
104 * Check for 'Realm' attribute. If it exists, then we've proxied
105 * it already ( via another rlm_realm instance ) and should return.
108 if (fr_pair_find_by_num(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
109 RDEBUG2("Request already has destination realm set. Ignoring");
110 return RLM_MODULE_NOOP;
114 * We will be modifing this later, so we want our own copy
117 namebuf = talloc_typed_strdup(request, request->username->vp_strvalue);
120 switch (inst->format) {
121 case REALM_FORMAT_SUFFIX:
122 RDEBUG2("Checking for suffix after \"%c\"", inst->delim[0]);
123 ptr = strrchr(username, inst->delim[0]);
130 case REALM_FORMAT_PREFIX:
131 RDEBUG2("Checking for prefix before \"%c\"", inst->delim[0]);
132 ptr = strchr(username, inst->delim[0]);
136 realmname = username;
147 * Print out excruciatingly descriptive debugging messages
148 * for the people who find it too difficult to think about
152 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
153 realmname, request->username->vp_strvalue);
155 if (inst->ignore_null ) {
156 RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
157 inst->delim[0], request->username->vp_strvalue);
158 talloc_free(namebuf);
159 return RLM_MODULE_NOOP;
161 RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
162 inst->delim[0], request->username->vp_strvalue);
166 * Allow DEFAULT realms unless told not to.
168 realm = realm_find(realmname);
170 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
172 * Try querying for the dynamic realm.
174 if (!realm && inst->trust_router) {
175 realm = tr_query_realm(request, realmname, inst->default_community, inst->rp_realm, inst->trust_router, inst->tr_port);
177 RDEBUG2("No trust router configured, skipping dynamic realm lookup");
182 RDEBUG2("No such realm \"%s\"", (!realmname) ? "NULL" : realmname);
183 talloc_free(namebuf);
184 return RLM_MODULE_NOOP;
187 if (inst->ignore_default && (strcmp(realm->name, "DEFAULT")) == 0) {
188 RDEBUG2("Found DEFAULT, but skipping due to config");
189 talloc_free(namebuf);
190 return RLM_MODULE_NOOP;
193 RDEBUG2("Found realm \"%s\"", realm->name);
196 * If we've been told to strip the realm off, then do so.
198 if (realm->strip_realm) {
200 * Create the Stripped-User-Name attribute, if it
204 if (request->username->da->attr != PW_STRIPPED_USER_NAME) {
205 vp = radius_pair_create(request->packet, &request->packet->vps,
206 PW_STRIPPED_USER_NAME, 0);
207 RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
209 vp = request->username;
210 RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
213 fr_pair_value_strcpy(vp, username);
214 request->username = vp;
218 * Add the realm name to the request.
219 * If the realm is a regex, the use the realm as entered
220 * by the user. Otherwise, use the configured realm name,
221 * as realm name comparison is case insensitive. We want
222 * to use the configured name, rather than what the user
225 if (realm->name[0] != '~') realmname = realm->name;
228 * A NULL realmname is allowed.
231 pair_make_request("Realm", realmname, T_OP_EQ);
232 RDEBUG2("Adding Realm = \"%s\"", realmname);
235 talloc_free(namebuf);
239 * Figure out what to do with the request.
241 switch (request->packet->code) {
243 RDEBUG2("Unknown packet code %d\n",
244 request->packet->code);
245 return RLM_MODULE_NOOP;
248 * Perhaps accounting proxying was turned off.
250 case PW_CODE_ACCOUNTING_REQUEST:
251 if (!realm->acct_pool) {
252 RDEBUG2("Accounting realm is LOCAL");
253 return RLM_MODULE_OK;
258 * Perhaps authentication proxying was turned off.
260 case PW_CODE_ACCESS_REQUEST:
261 if (!realm->auth_pool) {
262 RDEBUG2("Authentication realm is LOCAL");
263 return RLM_MODULE_OK;
269 RDEBUG2("Proxying request from user %s to realm %s",
270 request->username->vp_strvalue, realm->name);
273 * Skip additional checks if it's not an accounting
276 if (request->packet->code != PW_CODE_ACCOUNTING_REQUEST) {
277 *returnrealm = realm;
278 return RLM_MODULE_UPDATED;
282 * FIXME: Each server should have a unique server key,
283 * and put it in the accounting packet. Every server
284 * should know about the keys, and NOT proxy requests to
285 * a server with key X if the packet already contains key
290 * If this request has arrived from another freeradius server
291 * that has already proxied the request, we don't need to do
294 vp = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
295 if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
297 fr_ipaddr_t my_ipaddr;
299 my_ipaddr.af = AF_INET;
300 my_ipaddr.prefix = 32;
301 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
304 * Loop over the home accounting servers for this
305 * realm. If one of them has the same IP as the
306 * FreeRADIUS-Proxied-To attribute, then the
307 * packet has already been sent there. Don't
308 * send it there again.
310 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
311 if (realm->acct_pool->servers[i]->ipaddr.af == AF_UNSPEC) continue;
313 if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &my_ipaddr) == 0) {
314 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
315 return RLM_MODULE_OK;
320 * See detail_recv() in src/main/listen.c for the
324 } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
325 !fr_inaddr_any(&request->packet->src_ipaddr)) {
329 * Loop over the home accounting servers for this
330 * realm. If one of them has the same IP as the
331 * FreeRADIUS-Proxied-To attribute, then the
332 * packet has already been sent there. Don't
333 * send it there again.
335 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
336 if (realm->acct_pool->servers[i]->ipaddr.af == AF_UNSPEC) continue;
338 if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
339 &request->packet->src_ipaddr) == 0) &&
340 (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
341 RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
342 return RLM_MODULE_OK;
345 #endif /* WITH_DETAIL */
347 #endif /* WITH_PROXY */
350 * We got this far, which means we have a realm, set returnrealm
352 *returnrealm = realm;
354 return RLM_MODULE_UPDATED;
358 * Perform the realm module instantiation. Configuration info is
359 * stored in *instance for later use.
362 static int mod_instantiate(CONF_SECTION *conf, void *instance)
364 struct rlm_realm_t *inst = instance;
366 if (strcasecmp(inst->format_string, "suffix") == 0) {
367 inst->format = REALM_FORMAT_SUFFIX;
369 } else if (strcasecmp(inst->format_string, "prefix") == 0) {
370 inst->format = REALM_FORMAT_PREFIX;
373 cf_log_err_cs(conf, "Invalid value \"%s\" for format",
374 inst->format_string);
378 if (cf_new_escape && (strcmp(inst->delim, "\\\\") == 0)) {
382 if (strlen(inst->delim) != 1) {
383 cf_log_err_cs(conf, "Invalid value \"%s\" for delimiter",
388 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
389 /* initialize the trust router integration code */
390 if (strcmp(inst->trust_router, "none") != 0) {
391 if (!tr_init()) return -1;
393 rad_const_free(inst->trust_router);
394 inst->trust_router = NULL;
403 * Examine a request for a username with an realm, and if it
404 * corresponds to something in the realms file, set that realm as
407 * This should very nearly duplicate the old proxy_send() code
409 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
415 * Check if we've got to proxy the request.
416 * If not, return without adding a Proxy-To-Realm
419 rcode = check_for_realm(instance, request, &realm);
420 if (rcode != RLM_MODULE_UPDATED) return rcode;
421 if (!realm) return RLM_MODULE_NOOP;
424 * Maybe add a Proxy-To-Realm attribute to the request.
426 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
428 pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
430 return RLM_MODULE_UPDATED; /* try the next module */
434 * This does the exact same thing as the mod_authorize, it's just called
437 static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
442 if (!request->username) {
443 return RLM_MODULE_NOOP;
447 * Check if we've got to proxy the request.
448 * If not, return without adding a Proxy-To-Realm
451 rcode = check_for_realm(instance, request, &realm);
452 if (rcode != RLM_MODULE_UPDATED) return rcode;
453 if (!realm) return RLM_MODULE_NOOP;
456 * Maybe add a Proxy-To-Realm attribute to the request.
458 RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
460 pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
462 return RLM_MODULE_UPDATED; /* try the next module */
467 * CoA realms via Operator-Name. Because the realm isn't in a
468 * User-Name, concepts like "prefix" and "suffix' don't matter.
470 static rlm_rcode_t mod_realm_recv_coa(UNUSED void *instance, REQUEST *request)
475 if (fr_pair_find_by_num(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
476 RDEBUG2("Request already has destination realm set. Ignoring");
477 return RLM_MODULE_NOOP;
480 vp = fr_pair_find_by_num(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
481 if (!vp) return RLM_MODULE_NOOP;
484 * Catch the case of broken dictionaries.
486 if (vp->da->type != PW_TYPE_STRING) return RLM_MODULE_NOOP;
489 * The string is too short.
491 if (vp->vp_length == 1) return RLM_MODULE_NOOP;
494 * '1' means "the rest of the string is a realm"
496 if (vp->vp_strvalue[0] != '1') return RLM_MODULE_NOOP;
498 realm = realm_find(vp->vp_strvalue + 1);
499 if (!realm) return RLM_MODULE_NOTFOUND;
501 if (!realm->coa_pool) {
502 RDEBUG2("CoA realm is LOCAL");
503 return RLM_MODULE_OK;
507 * Maybe add a Proxy-To-Realm attribute to the request.
509 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
511 pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
513 return RLM_MODULE_UPDATED; /* try the next module */
517 /* globally exported name */
518 extern module_t rlm_realm;
519 module_t rlm_realm = {
520 .magic = RLM_MODULE_INIT,
522 .type = RLM_TYPE_HUP_SAFE,
523 .inst_size = sizeof(struct rlm_realm_t),
524 .config = module_config,
525 .instantiate = mod_instantiate,
527 [MOD_AUTHORIZE] = mod_authorize,
528 [MOD_PREACCT] = mod_preacct,
530 [MOD_RECV_COA] = mod_realm_recv_coa