Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_realm / rlm_realm.c
index f895cdd..e960a26 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * rlm_realm.c 
+ * rlm_realm.c
  *
  * Version:    $Id$
  *
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  * FIXME add copyrights
  */
 
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#include "libradius.h"
-#include "radiusd.h"
-#include "modules.h"
-
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
 
 #define  REALM_FORMAT_PREFIX   0
 #define  REALM_FORMAT_SUFFIX   1
@@ -48,6 +34,8 @@ typedef struct realm_config_t {
         int        format;
         char       *formatstring;
         char       *delim;
+       int        ignore_default;
+       int        ignore_null;
 } realm_config_t;
 
 static CONF_PARSER module_config[] = {
@@ -55,17 +43,21 @@ static CONF_PARSER module_config[] = {
     offsetof(realm_config_t,formatstring), NULL, "suffix" },
   { "delimiter", PW_TYPE_STRING_PTR,
     offsetof(realm_config_t,delim), NULL, "@" },
+  { "ignore_default", PW_TYPE_BOOLEAN,
+    offsetof(realm_config_t,ignore_default), NULL, "no" },
+  { "ignore_null", PW_TYPE_BOOLEAN,
+    offsetof(realm_config_t,ignore_null), NULL, "no" },
   { NULL, -1, 0, NULL, NULL }    /* end the list */
 };
 
 /*
  *     Internal function to cut down on duplicated code.
  *
- *     Returns NULL on don't proxy, realm otherwise.
+ *     Returns -1 on failure, 0 on no failure.  returnrealm
+ *     is NULL on don't proxy, realm otherwise.
  */
-static REALM *check_for_realm(void *instance, REQUEST *request)
+static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm)
 {
-       int is_local;
        char namebuf[MAX_STRING_LEN];
        char *username;
        char *realmname = NULL;
@@ -75,6 +67,9 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
 
         struct realm_config_t *inst = instance;
 
+       /* initiate returnrealm */
+       *returnrealm = NULL;
+
        /*
         *      If the request has a proxy entry, then it's a proxy
         *      reply, and we're walking through the module list again.
@@ -85,46 +80,48 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
         *      Also, if there's no User-Name attribute, we can't
         *      proxy it, either.
         */
+#ifdef WITH_PROXY
        if ((request->proxy != NULL) ||
            (request->username == NULL)) {
-               DEBUG2("    rlm_realm: Proxy reply, or no user name.  Ignoring.");
-               return NULL;
+               RDEBUG2("Proxy reply, or no User-Name.  Ignoring.");
+               return RLM_MODULE_OK;
        }
+#endif
 
        /*
         *      Check for 'Realm' attribute.  If it exists, then we've proxied
         *      it already ( via another rlm_realm instance ) and should return.
         */
 
-       if ( (vp = pairfind(request->packet->vps, PW_REALM)) != NULL ) {
-               DEBUG2("    rlm_realm: Request already proxied.  Ignoring.");
-               return NULL;
+       if (pairfind(request->packet->vps, PW_REALM, 0) != NULL ) {
+               RDEBUG2("Request already proxied.  Ignoring.");
+               return RLM_MODULE_OK;
        }
 
        /*
         *      We will be modifing this later, so we want our own copy
         *      of it.
         */
-       strNcpy(namebuf, (char *)request->username->strvalue, sizeof(namebuf));
+       strlcpy(namebuf, (char *)request->username->vp_strvalue, sizeof(namebuf));
        username = namebuf;
 
        switch(inst->format)
        {
 
        case REALM_FORMAT_SUFFIX:
-         
+
          /* DEBUG2("  rlm_realm: Checking for suffix after \"%c\"", inst->delim[0]); */
-               realmname = strrchr(username, inst->delim[0]);          
+               realmname = strrchr(username, inst->delim[0]);
                if (realmname) {
                        *realmname = '\0';
                        realmname++;
                }
                break;
-               
+
        case REALM_FORMAT_PREFIX:
-               
+
                /* DEBUG2("  rlm_realm: Checking for prefix before \"%c\"", inst->delim[0]); */
-               
+
                ptr = strchr(username, inst->delim[0]);
                if (ptr) {
                        *ptr = '\0';
@@ -133,27 +130,47 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
                     username = ptr;
                }
                break;
-              
+
        default:
                realmname = NULL;
                break;
        }
 
-       DEBUG2("    rlm_realm: Looking up realm %s for User-Name = \"%s\"",
-              (realmname == NULL) ? "NULL" : realmname,
-              request->username->strvalue);
+       /*
+        *      Print out excruciatingly descriptive debugging messages
+        *      for the people who find it too difficult to think about
+        *      what's going on.
+        */
+       if (realmname) {
+               RDEBUG2("Looking up realm \"%s\" for User-Name = \"%s\"",
+                      realmname, request->username->vp_strvalue);
+       } else {
+               if( inst->ignore_null ) {
+                       RDEBUG2("No '%c' in User-Name = \"%s\", skipping NULL due to config.",
+                       inst->delim[0], request->username->vp_strvalue);
+                       return RLM_MODULE_NOOP;
+               }
+               RDEBUG2("No '%c' in User-Name = \"%s\", looking up realm NULL",
+                      inst->delim[0], request->username->vp_strvalue);
+       }
 
        /*
-        *      Allow NULL realms.
+        *      Allow DEFAULT realms unless told not to.
         */
-       realm = realm_find(realmname, (request->packet->code == PW_ACCOUNTING_REQUEST));
+       realm = realm_find(realmname);
        if (!realm) {
-               DEBUG2("    rlm_realm: No such realm %s",
+               RDEBUG2("No such realm \"%s\"",
                       (realmname == NULL) ? "NULL" : realmname);
-               return NULL;
+               return RLM_MODULE_NOOP;
        }
-       DEBUG2("    rlm_realm: Found realm %s", realm->realm);
-       
+       if( inst->ignore_default &&
+           (strcmp(realm->name, "DEFAULT")) == 0) {
+               RDEBUG2("Found DEFAULT, but skipping due to config.");
+               return RLM_MODULE_NOOP;
+       }
+
+       RDEBUG2("Found realm \"%s\"", realm->name);
+
        /*
         *      If we've been told to strip the realm off, then do so.
         */
@@ -164,48 +181,49 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
                 *
                 */
                if (request->username->attribute != PW_STRIPPED_USER_NAME) {
-                       vp = paircreate(PW_STRIPPED_USER_NAME, PW_TYPE_STRING);
-                       if (!vp) {
-                               radlog(L_ERR|L_CONS, "no memory");
-                               exit(1);
-                       }
-                       pairadd(&request->packet->vps, vp);
-                       DEBUG2("    rlm_realm: Adding Stripped-User-Name = \"%s\"", username);
+                       vp = radius_paircreate(request, &request->packet->vps,
+                                              PW_STRIPPED_USER_NAME, 0,
+                                              PW_TYPE_STRING);
+                       RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
                } else {
                        vp = request->username;
-                       DEBUG2("    rlm_realm: Setting Stripped-User-Name = \"%s\"", username);
+                       RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
                }
 
-               strcpy(vp->strvalue, username);
-               vp->length = strlen((char *)vp->strvalue);
+               strcpy(vp->vp_strvalue, username);
+               vp->length = strlen((char *)vp->vp_strvalue);
                request->username = vp;
        }
 
-       DEBUG2("  rlm_realm: Proxying request from user %s to realm %s",
-              username, realm->realm);
+       /*
+        *      Add the realm name to the request.
+        *      If the realm is a regex, the use the realm as entered
+        *      by the user.  Otherwise, use the configured realm name,
+        *      as realm name comparison is case insensitive.  We want
+        *      to use the configured name, rather than what the user
+        *      entered.
+        */
+       if (realm->name[0] != '~') realmname = realm->name;
+       pairadd(&request->packet->vps, pairmake("Realm", realmname,
+                                               T_OP_EQ));
+       RDEBUG2("Adding Realm = \"%s\"", realmname);
 
        /*
         *      Figure out what to do with the request.
         */
-       is_local = FALSE;
        switch (request->packet->code) {
        default:
-               DEBUG2("rlm_realm: Unknown packet code %d\n",
+               RDEBUG2("Unknown packet code %d\n",
                       request->packet->code);
-               return NULL;            /* don't do anything */
-               
+               return RLM_MODULE_OK;           /* don't do anything */
+
                /*
                 *      Perhaps accounting proxying was turned off.
                 */
        case PW_ACCOUNTING_REQUEST:
-               if (realm->acct_ipaddr == htonl(INADDR_NONE)) {
-                       DEBUG2("rlm_realm:  Accounting realm is LOCAL.");
-                       is_local = TRUE;
-               }
-
-               if (realm->acct_port == 0) {
-                       DEBUG2("rlm_realm:  acct_port is not set.  proxy cancelled");
-                       return NULL;
+               if (!realm->acct_pool) {
+                       RDEBUG2("Accounting realm is LOCAL.");
+                       return RLM_MODULE_OK;
                }
                break;
 
@@ -213,33 +231,96 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
                 *      Perhaps authentication proxying was turned off.
                 */
        case PW_AUTHENTICATION_REQUEST:
-               if (realm->ipaddr == htonl(INADDR_NONE)) {
-                       DEBUG2("rlm_realm:  Authentication realm is LOCAL.");
-                       is_local = TRUE;
-               }
-
-               if (realm->auth_port == 0) {
-                       DEBUG2("rlm_realm:  auth_port is not set.  proxy cancelled");
-                       return NULL;
+               if (!realm->auth_pool) {
+                       RDEBUG2("Authentication realm is LOCAL.");
+                       return RLM_MODULE_OK;
                }
                break;
        }
 
+#ifdef WITH_PROXY
+       RDEBUG2("Proxying request from user %s to realm %s",
+              username, realm->name);
+
        /*
-        *      Add the realm name to the request.
+        *      Skip additional checks if it's not an accounting
+        *      request.
         */
-       pairadd(&request->packet->vps, pairmake("Realm", realm->realm,
-                                               T_OP_EQ));
-       DEBUG2("    rlm_realm: Adding Realm = \"%s\"", realm->realm);
+       if (request->packet->code != PW_ACCOUNTING_REQUEST) {
+               *returnrealm = realm;
+               return RLM_MODULE_UPDATED;
+       }
 
        /*
-        *      Local realm, don't proxy it.
+        *      FIXME: Each server should have a unique server key,
+        *      and put it in the accounting packet.  Every server
+        *      should know about the keys, and NOT proxy requests to
+        *      a server with key X if the packet already contains key
+        *      X.
         */
-       if (is_local) {
-               return NULL;
+
+       /*
+        *      If this request has arrived from another freeradius server
+        *      that has already proxied the request, we don't need to do
+        *      it again.
+        */
+       vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0);
+       if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
+               int i;
+               fr_ipaddr_t my_ipaddr;
+
+               my_ipaddr.af = AF_INET;
+               my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+
+               /*
+                *      Loop over the home accounting servers for this
+                *      realm.  If one of them has the same IP as the
+                *      FreeRADIUS-Proxied-To attribute, then the
+                *      packet has already been sent there.  Don't
+                *      send it there again.
+                */
+               for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
+                       if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
+                                           &my_ipaddr) == 0) {
+                               RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
+                               return RLM_MODULE_OK;
+                       }
+               }
+
+               /*
+                *      See detail_recv() in src/main/listen.c for the
+                *      additional checks.
+                */
+       } else if ((request->listener->type == RAD_LISTEN_DETAIL) &&
+                  ((request->packet->src_ipaddr.af == AF_INET6) ||
+                   (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)))) {
+               int i;
+
+               /*
+                *      Loop over the home accounting servers for this
+                *      realm.  If one of them has the same IP as the
+                *      FreeRADIUS-Proxied-To attribute, then the
+                *      packet has already been sent there.  Don't
+                *      send it there again.
+                */
+               for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
+                       if ((fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
+                                            &request->packet->src_ipaddr) == 0) &&
+                           (realm->acct_pool->servers[i]->port == request->packet->src_port)) {
+                               RDEBUG2("Suppressing proxy because packet was already sent to a server in that realm");
+                               return RLM_MODULE_OK;
+                       }
+               }
+
        }
+#endif
 
-       return realm;
+       /*
+        *      We got this far, which means we have a realm, set returnrealm
+        */
+       *returnrealm = realm;
+
+       return RLM_MODULE_UPDATED;
 }
 
 /*
@@ -253,12 +334,12 @@ static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
         *      Tell the server to proxy this request to another
         *      realm.
         */
-       vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
+       vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
        if (!vp) {
                radlog(L_ERR|L_CONS, "no memory");
                exit(1);
        }
-       
+
        /*
         *  Add it, even if it's already present.
         */
@@ -275,7 +356,11 @@ static int realm_instantiate(CONF_SECTION *conf, void **instance)
         struct realm_config_t *inst;
 
         /* setup a storage area for instance data */
-        inst = rad_malloc(sizeof(struct realm_config_t));
+        inst = rad_malloc(sizeof(*inst));
+       if (!inst) {
+               return -1;
+       }
+       memset(inst, 0, sizeof(*inst));
 
        if(cf_section_parse(conf, inst, module_config) < 0) {
               free(inst);
@@ -291,7 +376,6 @@ static int realm_instantiate(CONF_SECTION *conf, void **instance)
             free(inst);
             return -1;
        }
-       free(inst->formatstring);
        if(strlen(inst->delim) != 1) {
             radlog(L_ERR, "Bad value \"%s\" for realm delimiter value", inst->delim);
             free(inst);
@@ -305,7 +389,7 @@ static int realm_instantiate(CONF_SECTION *conf, void **instance)
 
 
 
+
 
 /*
  *  Examine a request for a username with an realm, and if it
@@ -316,6 +400,7 @@ static int realm_instantiate(CONF_SECTION *conf, void **instance)
  */
 static int realm_authorize(void *instance, REQUEST *request)
 {
+       int rcode;
        REALM *realm;
 
        /*
@@ -323,16 +408,15 @@ static int realm_authorize(void *instance, REQUEST *request)
         *      If not, return without adding a Proxy-To-Realm
         *      attribute.
         */
-       realm = check_for_realm(instance, request);
-       if (!realm) {
-               return RLM_MODULE_NOOP;
-       }
+       rcode = check_for_realm(instance, request, &realm);
+       if (rcode != RLM_MODULE_UPDATED) return rcode;
+       if (!realm) return RLM_MODULE_NOOP;
 
        /*
         *      Maybe add a Proxy-To-Realm attribute to the request.
         */
-       DEBUG2("rlm_realm:  Preparing to proxy authentication request to realm %s\n",
-              realm->realm);
+       RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
+              realm->name);
        add_proxy_to_realm(&request->config_items, realm);
 
        return RLM_MODULE_UPDATED; /* try the next module */
@@ -344,55 +428,54 @@ static int realm_authorize(void *instance, REQUEST *request)
  */
 static int realm_preacct(void *instance, REQUEST *request)
 {
-       const char *name = (char *)request->username->strvalue;
+       int rcode;
+       const char *name = (char *)request->username->vp_strvalue;
        REALM *realm;
 
        if (!name)
          return RLM_MODULE_OK;
-       
+
 
        /*
         *      Check if we've got to proxy the request.
         *      If not, return without adding a Proxy-To-Realm
         *      attribute.
         */
-       realm = check_for_realm(instance, request);
-       if (!realm) {
-               return RLM_MODULE_NOOP;
-       }
-
+       rcode = check_for_realm(instance, request, &realm);
+       if (rcode != RLM_MODULE_UPDATED) return rcode;
+       if (!realm) return RLM_MODULE_NOOP;
 
        /*
         *      Maybe add a Proxy-To-Realm attribute to the request.
         */
-       DEBUG2("rlm_realm:  Preparing to proxy accounting request to realm %s\n",
-              realm->realm);
+       RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
+              realm->name);
        add_proxy_to_realm(&request->config_items, realm);
 
-       return RLM_MODULE_OK; /* try the next module */
+       return RLM_MODULE_UPDATED; /* try the next module */
 }
 
 static int realm_detach(void *instance)
 {
-       struct realm_config_t *inst = instance;
-       free(inst->delim);
        free(instance);
        return 0;
 }
 
 /* globally exported name */
 module_t rlm_realm = {
-  "realm",
-  0,                           /* type: reserved */
-  NULL,                                /* initialization */
-  realm_instantiate,           /* instantiation */
-  {
-         NULL,                 /* authentication */
-         realm_authorize,      /* authorization */
-         realm_preacct,        /* preaccounting */
-         NULL,                 /* accounting */
-         NULL                  /* checksimul */
-  },
-  realm_detach,                        /* detach */
-  NULL,                                /* destroy */
+       RLM_MODULE_INIT,
+       "realm",
+       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       realm_instantiate,              /* instantiation */
+       realm_detach,                   /* detach */
+       {
+               NULL,                   /* authentication */
+               realm_authorize,        /* authorization */
+               realm_preacct,  /* preaccounting */
+               NULL,                   /* accounting */
+               NULL,                   /* checksimul */
+               NULL,                   /* pre-proxy */
+               NULL,                   /* post-proxy */
+               NULL                    /* post-auth */
+       },
 };