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
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
28 #include "trustrouter_integ.h"
30 #define REALM_FORMAT_PREFIX 0
31 #define REALM_FORMAT_SUFFIX 1
33 typedef struct realm_config_t {
39 char *default_community;
45 #define stringify(s) #s
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 */
69 * Internal function to cut down on duplicated code.
71 * Returns -1 on failure, 0 on no failure. returnrealm
72 * is NULL on don't proxy, realm otherwise.
74 static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
78 char const *realmname = NULL;
83 struct realm_config_t *inst = instance;
85 /* initiate returnrealm */
89 * If the request has a proxy entry, then it's a proxy
90 * reply, and we're walking through the module list again.
92 * In that case, don't bother trying to proxy the request
95 * Also, if there's no User-Name attribute, we can't
98 if ((!request->username)
100 || (request->proxy != NULL)
104 RDEBUG2("Proxy reply, or no User-Name. Ignoring.");
105 return RLM_MODULE_OK;
109 * Check for 'Realm' attribute. If it exists, then we've proxied
110 * it already ( via another rlm_realm instance ) and should return.
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;
119 * We will be modifing this later, so we want our own copy
122 namebuf = talloc_strdup(request, request->username->vp_strvalue);
125 switch(inst->format) {
126 case REALM_FORMAT_SUFFIX:
128 /* DEBUG2(" rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
129 ptr = strrchr(username, inst->delim[0]);
136 case REALM_FORMAT_PREFIX:
138 /* DEBUG2(" rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
140 ptr = strchr(username, inst->delim[0]);
144 realmname = username;
155 * Print out excruciatingly descriptive debugging messages
156 * for the people who find it too difficult to think about
160 RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
161 realmname, request->username->vp_strvalue);
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;
169 RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
170 inst->delim[0], request->username->vp_strvalue);
174 * Allow DEFAULT realms unless told not to.
176 realm = realm_find(realmname);
178 realm = tr_query_realm(realmname, inst->default_community, inst->rp_realm, inst->trust_router, inst->tr_port);
180 RDEBUG2("No such realm \"%s\"",
181 (!realmname) ? "NULL" : realmname);
182 talloc_free(namebuf);
183 return RLM_MODULE_NOOP;
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;
192 RDEBUG2("Found realm \"%s\"", realm->name);
195 * If we've been told to strip the realm off, then do so.
197 if (realm->striprealm) {
199 * Create the Stripped-User-Name attribute, if it
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);
208 vp = request->username;
209 RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
212 pairstrcpy(vp, username);
213 request->username = vp;
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
224 if (realm->name[0] != '~') realmname = realm->name;
225 pairmake_packet("Realm", realmname, T_OP_EQ);
226 RDEBUG2("Adding Realm = \"%s\"", realmname);
228 talloc_free(namebuf);
229 realmname = username = NULL;
232 * Figure out what to do with the request.
234 switch (request->packet->code) {
236 RDEBUG2("Unknown packet code %d\n",
237 request->packet->code);
238 return RLM_MODULE_OK; /* don't do anything */
241 * Perhaps accounting proxying was turned off.
243 case PW_ACCOUNTING_REQUEST:
244 if (!realm->acct_pool) {
245 RDEBUG2("Accounting realm is LOCAL.");
246 return RLM_MODULE_OK;
251 * Perhaps authentication proxying was turned off.
253 case PW_AUTHENTICATION_REQUEST:
254 if (!realm->auth_pool) {
255 RDEBUG2("Authentication realm is LOCAL.");
256 return RLM_MODULE_OK;
262 RDEBUG2("Proxying request from user %s to realm %s",
263 request->username->vp_strvalue, realm->name);
266 * Skip additional checks if it's not an accounting
269 if (request->packet->code != PW_ACCOUNTING_REQUEST) {
270 *returnrealm = realm;
271 return RLM_MODULE_UPDATED;
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
283 * If this request has arrived from another freeradius server
284 * that has already proxied the request, we don't need to do
287 vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
288 if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
290 fr_ipaddr_t my_ipaddr;
292 my_ipaddr.af = AF_INET;
293 my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
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.
302 for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
303 if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
305 RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
306 return RLM_MODULE_OK;
311 * See detail_recv() in src/main/listen.c for the
315 } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
316 !fr_inaddr_any(&request->packet->src_ipaddr)) {
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.
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;
334 #endif /* WITH_DETAIL */
336 #endif /* WITH_PROXY */
339 * We got this far, which means we have a realm, set returnrealm
341 *returnrealm = realm;
343 return RLM_MODULE_UPDATED;
347 * Perform the realm module instantiation. Configuration info is
348 * stored in *instance for later use.
351 static int mod_instantiate(CONF_SECTION *conf, void *instance)
353 struct realm_config_t *inst = instance;
355 /* initialize the trust router integration code */
358 if (strcasecmp(inst->formatstring, "suffix") == 0) {
360 inst->format = REALM_FORMAT_SUFFIX;
362 } else if (strcasecmp(inst->formatstring, "prefix") == 0) {
363 inst->format = REALM_FORMAT_PREFIX;
366 cf_log_err_cs(conf, "Invalid value \"%s\" for format",
371 if (strlen(inst->delim) != 1) {
372 cf_log_err_cs(conf, "Invalid value \"%s\" for delimiter",
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
386 * This should very nearly duplicate the old proxy_send() code
388 static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
394 * Check if we've got to proxy the request.
395 * If not, return without adding a Proxy-To-Realm
398 rcode = check_for_realm(instance, request, &realm);
399 if (rcode != RLM_MODULE_UPDATED) return rcode;
400 if (!realm) return RLM_MODULE_NOOP;
403 * Maybe add a Proxy-To-Realm attribute to the request.
405 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
407 pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
409 return RLM_MODULE_UPDATED; /* try the next module */
413 * This does the exact same thing as the mod_authorize, it's just called
416 static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
421 if (!request->username) {
422 return RLM_MODULE_NOOP;
426 * Check if we've got to proxy the request.
427 * If not, return without adding a Proxy-To-Realm
430 rcode = check_for_realm(instance, request, &realm);
431 if (rcode != RLM_MODULE_UPDATED) return rcode;
432 if (!realm) return RLM_MODULE_NOOP;
435 * Maybe add a Proxy-To-Realm attribute to the request.
437 RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
439 pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
441 return RLM_MODULE_UPDATED; /* try the next module */
446 * CoA realms via Operator-Name. Because the realm isn't in a
447 * User-Name, concepts like "prefix" and "suffix' don't matter.
449 static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
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;
459 vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
460 if (!vp) return RLM_MODULE_NOOP;
463 * Catch the case of broken dictionaries.
465 if (vp->da->type != PW_TYPE_STRING) return RLM_MODULE_NOOP;
468 * The string is too short.
470 if (vp->length == 1) return RLM_MODULE_NOOP;
473 * '1' means "the rest of the string is a realm"
475 if (vp->vp_strvalue[0] != '1') return RLM_MODULE_NOOP;
477 realm = realm_find(vp->vp_strvalue + 1);
478 if (!realm) return RLM_MODULE_NOTFOUND;
481 if (!realm->coa_pool) {
482 RDEBUG2("CoA realm is LOCAL.");
483 return RLM_MODULE_OK;
487 * Maybe add a Proxy-To-Realm attribute to the request.
489 RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
491 pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
493 return RLM_MODULE_UPDATED; /* try the next module */
497 /* globally exported name */
498 module_t rlm_realm = {
501 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
502 sizeof(struct realm_config_t),
504 mod_instantiate, /* instantiation */
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 */
516 , realm_recv_coa, /* recv-coa */