Enable building #WITHOUT_PROXY
[freeradius.git] / src / modules / rlm_attr_rewrite / rlm_attr_rewrite.c
index 2e95079..fd47702 100644 (file)
  *
  *   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 2002  The FreeRADIUS server project
+ * Copyright 2002,2006  The FreeRADIUS server project
  * Copyright 2002  Kostas Kalevras <kkalev@noc.ntua.gr>
  */
 
-#include "config.h"
-#include "autoconf.h"
-#include "libradius.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "config.h"
-#if HAVE_REGEX_H
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+
+#ifdef HAVE_REGEX_H
 #      include <regex.h>
 #endif
 
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
-
 #define RLM_REGEX_INPACKET 0
 #define RLM_REGEX_INCONFIG 1
 #define RLM_REGEX_INREPLY  2
-
-static const char rcsid[] = "$Id$";
+#define RLM_REGEX_INPROXY 3
+#define RLM_REGEX_INPROXYREPLY 4
 
 typedef struct rlm_attr_rewrite_t {
        char *attribute;        /* The attribute to search for */
-       int  attr_num;          /* The attribute number */
+       DICT_ATTR *da;          /* The attribute definition */
        char *search;           /* The pattern to search for */
        int search_len;         /* The length of the search pattern */
-       char *searchin_str;     /* The VALUE_PAIR list to search in. Can be either packet,reply or config */
+       char *searchin_str;     /* The VALUE_PAIR list to search in. Can be either packet,reply,proxy,proxy_reply or control (plus it's alias 'config') */
        char searchin;          /* The same as above just coded as a number for speed */
        char *replace;          /* The replacement */
        int replace_len;        /* The length of the replacement string */
-       int  append;            /* Switch to control append mode (1,0) */ 
+       int  append;            /* Switch to control append mode (1,0) */
        int  nocase;            /* Ignore case */
        int  new_attr;          /* Boolean. Do we create a new attribute or not? */
        int  num_matches;       /* Maximum number of matches */
-       char *name;             /* The module name */
+       const char *name;       /* The module name */
 } rlm_attr_rewrite_t;
 
-
-static CONF_PARSER module_config[] = {
+static const CONF_PARSER module_config[] = {
   { "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
   { "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
   { "searchin",  PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
@@ -72,13 +65,11 @@ static CONF_PARSER module_config[] = {
   { NULL, -1, 0, NULL, NULL }
 };
 
-
 static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
 {
        rlm_attr_rewrite_t *data;
        DICT_ATTR *dattr;
-       char *instance_name = NULL;
-       
+
        /*
         *      Set up a storage area for instance data
         */
@@ -98,7 +89,7 @@ static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
        }
 
        /*
-        *      Discover the attribute number of the key. 
+        *      Discover the attribute number of the key.
         */
        if (data->attribute == NULL) {
                radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
@@ -129,13 +120,20 @@ static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
                        data->searchin = RLM_REGEX_INPACKET;
                else if (strcmp(data->searchin_str, "config") == 0)
                        data->searchin = RLM_REGEX_INCONFIG;
+               else if (strcmp(data->searchin_str, "control") == 0)
+                       data->searchin = RLM_REGEX_INCONFIG;
                else if (strcmp(data->searchin_str, "reply") == 0)
                        data->searchin = RLM_REGEX_INREPLY;
+#ifdef WITH_PROXY
+               else if (strcmp(data->searchin_str, "proxy") == 0)
+                       data->searchin = RLM_REGEX_INPROXY;
+               else if (strcmp(data->searchin_str, "proxy_reply") == 0)
+                       data->searchin = RLM_REGEX_INPROXYREPLY;
+#endif
                else {
                        radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
                        data->searchin = RLM_REGEX_INPACKET;
                }
-               free((char *)data->searchin_str);
        }
        dattr = dict_attrbyname(data->attribute);
        if (dattr == NULL) {
@@ -143,16 +141,12 @@ static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
                                data->attribute);
                return -1;
        }
-       data->attr_num = dattr->attr;
+       data->da = dattr;
        /* Add the module instance name */
-       data->name = NULL;
-       instance_name = cf_section_name2(conf);
-       if (instance_name != NULL)
-               data->name = strdup(instance_name);
-       
-       
+       data->name = cf_section_name2(conf); /* may be NULL */
+
        *instance = data;
-       
+
        return 0;
 }
 
@@ -163,31 +157,80 @@ static int do_attr_rewrite(void *instance, REQUEST *request)
        VALUE_PAIR *attr_vp = NULL;
        VALUE_PAIR *tmp = NULL;
        regex_t preg;
-       regmatch_t pmatch;
+       regmatch_t pmatch[9];
        int cflags = 0;
        int err = 0;
        char done_xlat = 0;
        unsigned int len = 0;
        char err_msg[MAX_STRING_LEN];
        unsigned int i = 0;
+       unsigned int j = 0;
        unsigned int counter = 0;
        char new_str[MAX_STRING_LEN];
        char *ptr, *ptr2;
        char search_STR[MAX_STRING_LEN];
        char replace_STR[MAX_STRING_LEN];
-       int replace_len = 0;
 
-       if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE)) != NULL){
-               if (data->name == NULL || strcmp(data->name,attr_vp->strvalue))
+       if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE, 0)) != NULL){
+               if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue))
                        return RLM_MODULE_NOOP;
        }
 
-       if (!data->new_attr){
+       if (data->new_attr){
+               /* new_attribute = yes */
+               if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
+                       DEBUG2("%s: xlat on replace string failed.", data->name);
+                       return ret;
+               }
+               attr_vp = pairmake(data->attribute,replace_STR,0);
+               if (attr_vp == NULL){
+                       DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name,
+                               data->attribute,replace_STR);
+                       return ret;
+               }
+               switch(data->searchin){
+                       case RLM_REGEX_INPACKET:
+                               pairadd(&request->packet->vps,attr_vp);
+                               break;
+                       case RLM_REGEX_INCONFIG:
+                               pairadd(&request->config_items,attr_vp);
+                               break;
+                       case RLM_REGEX_INREPLY:
+                               pairadd(&request->reply->vps,attr_vp);
+                               break;
+#ifdef WITH_PROXY
+                       case RLM_REGEX_INPROXY:
+                               if (!request->proxy) {
+                                       pairbasicfree(attr_vp);
+                                       return RLM_MODULE_NOOP;
+                               }
+                               pairadd(&request->proxy->vps, attr_vp);
+                               break;
+                       case RLM_REGEX_INPROXYREPLY:
+                               if (!request->proxy_reply) {
+                                       pairbasicfree(attr_vp);
+                                       return RLM_MODULE_NOOP;
+                               }
+                               pairadd(&request->proxy_reply->vps, attr_vp);
+                               break;
+#endif
+                       default:
+                               radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
+                               data->searchin = RLM_REGEX_INPACKET;
+                               pairadd(&request->packet->vps,attr_vp);
+                               break;
+               }
+               DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR);
+               ret = RLM_MODULE_OK;
+       } else {
+               int replace_len = 0;
+
+               /* new_attribute = no */
                switch (data->searchin) {
                        case RLM_REGEX_INPACKET:
-                               if (data->attr_num == PW_USER_NAME)
+                               if (!data->da->vendor && (data->da->attr == PW_USER_NAME))
                                        attr_vp = request->username;
-                               else if (data->attr_num == PW_PASSWORD)
+                               else if (!data->da->vendor && (data->da->attr == PW_USER_PASSWORD))
                                        attr_vp = request->password;
                                else
                                        tmp = request->packet->vps;
@@ -198,21 +241,33 @@ static int do_attr_rewrite(void *instance, REQUEST *request)
                        case RLM_REGEX_INREPLY:
                                tmp = request->reply->vps;
                                break;
+#ifdef WITH_PROXY
+                       case RLM_REGEX_INPROXYREPLY:
+                               if (!request->proxy_reply)
+                                       return RLM_MODULE_NOOP;
+                               tmp = request->proxy_reply->vps;
+                               break;
+                       case RLM_REGEX_INPROXY:
+                               if (!request->proxy)
+                                       return RLM_MODULE_NOOP;
+                               tmp = request->proxy->vps;
+                               break;
+#endif
                        default:
-                               radlog(L_ERR, "rlm_attr_rewrite: Illegal value for searchin. Changing to packet.");
+                               radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
                                data->searchin = RLM_REGEX_INPACKET;
-                               attr_vp = pairfind(request->packet->vps, data->attr_num);
+                               attr_vp = pairfind(request->packet->vps, data->da->attr, data->da->vendor);
                                break;
                }
 do_again:
                if (tmp != NULL)
-                       attr_vp = pairfind(tmp, data->attr_num);
+                       attr_vp = pairfind(tmp, data->da->attr, data->da->vendor);
                if (attr_vp == NULL) {
-                       DEBUG2("rlm_attr_rewrite: Could not find value pair for attribute %s",data->attribute);
+                       DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute);
                        return ret;
                }
-               if (attr_vp->strvalue == NULL || attr_vp->length == 0){
-                       DEBUG2("rlm_attr_rewrite: Attribute %s string value NULL or of zero length",data->attribute);
+               if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){
+                       DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute);
                        return ret;
                }
                cflags |= REG_EXTENDED;
@@ -220,34 +275,33 @@ do_again:
                        cflags |= REG_ICASE;
 
                if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) {
-                       DEBUG2("rlm_attr_rewrite: xlat on search string failed.");
-                       return ret;
-               }
-       }
-       if (data->new_attr){
-               if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
-                       DEBUG2("rlm_attr_rewrite: xlat on replace string failed.");
+                       DEBUG2("%s: xlat on search string failed.", data->name);
                        return ret;
                }
-               replace_len = strlen(replace_STR);
-       }
 
-       if (!data->new_attr){
                if ((err = regcomp(&preg,search_STR,cflags))) {
                        regerror(err, &preg, err_msg, MAX_STRING_LEN);
-                       DEBUG2("rlm_attr_rewrite: regcomp() returned error: %s",err_msg);
+                       DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg);
                        return ret;
                }
+
+               if ((attr_vp->type == PW_TYPE_IPADDR) &&
+                   (attr_vp->vp_strvalue[0] == '\0')) {
+                       inet_ntop(AF_INET, &(attr_vp->vp_ipaddr),
+                                 attr_vp->vp_strvalue,
+                                 sizeof(attr_vp->vp_strvalue));
+               }
+
                ptr = new_str;
-               ptr2 = attr_vp->strvalue;
+               ptr2 = attr_vp->vp_strvalue;
                counter = 0;
 
-               for ( i = 0 ;i < data->num_matches; i++) {
-                       err = regexec(&preg, ptr2, 1, &pmatch, 0);
+               for ( i = 0 ;i < (unsigned)data->num_matches; i++) {
+                       err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
                        if (err == REG_NOMATCH) {
                                if (i == 0) {
-                                       DEBUG2("rlm_attr_rewrite: No match found for attribute %s with value '%s'",
-                                                       data->attribute, attr_vp->strvalue);
+                                       DEBUG2("%s: Does not match: %s = %s", data->name,
+                                                       data->attribute, attr_vp->vp_strvalue);
                                        regfree(&preg);
                                        goto to_do_again;
                                } else
@@ -255,32 +309,66 @@ do_again:
                        }
                        if (err != 0) {
                                regfree(&preg);
-                               radlog(L_ERR, "rlm_attr_rewrite: match failure for attribute %s with value '%s'",
-                                               data->attribute, attr_vp->strvalue);
+                               radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name,
+                                               data->attribute, attr_vp->vp_strvalue);
                                return ret;
                        }
-                       if (pmatch.rm_so == -1)
+                       if (pmatch[0].rm_so == -1)
                                break;
-                       len = pmatch.rm_so;
+                       len = pmatch[0].rm_so;
                        if (data->append) {
-                               len = len + (pmatch.rm_eo - pmatch.rm_so);
-                       } 
+                               len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
+                       }
                        counter += len;
                        if (counter >= MAX_STRING_LEN) {
                                regfree(&preg);
-                               DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
-                                               data->attribute, attr_vp->strvalue);    
+                               DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
+                                               data->attribute, attr_vp->vp_strvalue);
                                return ret;
                        }
 
-                       strncpy(ptr, ptr2,len);
+                       memcpy(ptr, ptr2,len);
                        ptr += len;
-                       ptr2 += pmatch.rm_eo;
+                       *ptr = '\0';
+                       ptr2 += pmatch[0].rm_eo;
+
+                       if (i == 0){
+                               /*
+                                * We only run on the first match, sorry
+                                */
+                               for(j = 0; j <= REQUEST_MAX_REGEX; j++){
+                                       char *p;
+                                       char buffer[sizeof(attr_vp->vp_strvalue)];
+
+                                       /*
+                                        * Stolen from src/main/valuepair.c, paircompare()
+                                        */
+
+                                       /*
+                                        * Delete old matches if the corresponding match does not
+                                        * exist in the current regex
+                                        */
+                                       if (pmatch[j].rm_so == -1){
+                                               p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
+                                               if (p){
+                                                       free(p);
+                                                       continue;
+                                               }
+                                               break;
+                                       }
+                                       memcpy(buffer,
+                                              attr_vp->vp_strvalue + pmatch[j].rm_so,
+                                              pmatch[j].rm_eo - pmatch[j].rm_so);
+                                       buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
+                                       p = strdup(buffer);
+                                       request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
+                               }
+                       }
 
                        if (!done_xlat){
                                if (data->replace_len != 0 &&
                                radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) {
-                                       DEBUG2("rlm_attr_rewrite: xlat on replace string failed.");
+                                       DEBUG2("%s: xlat on replace string failed.", data->name);
                                        return ret;
                                }
                                replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0;
@@ -290,29 +378,33 @@ do_again:
                        counter += replace_len;
                        if (counter >= MAX_STRING_LEN) {
                                regfree(&preg);
-                               DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
-                                               data->attribute, attr_vp->strvalue);    
+                               DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
+                                               data->attribute, attr_vp->vp_strvalue);
                                return ret;
                        }
                        if (replace_len){
-                               strncpy(ptr, replace_STR, replace_len);
-                               ptr += replace_len;     
+                               memcpy(ptr, replace_STR, replace_len);
+                               ptr += replace_len;
+                               *ptr = '\0';
                        }
                }
                regfree(&preg);
                len = strlen(ptr2) + 1;         /* We add the ending NULL */
                counter += len;
                if (counter >= MAX_STRING_LEN){
-                       DEBUG2("rlm_attr_rewrite: Replacement out of limits for attribute %s with value '%s'",
-                                       data->attribute, attr_vp->strvalue);    
+                       DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
+                                       data->attribute, attr_vp->vp_strvalue);
                        return ret;
                }
-               strncpy(ptr, ptr2, len);
+               memcpy(ptr, ptr2, len);
+               ptr[len] = '\0';
 
-               DEBUG2("rlm_attr_rewrite: Changed value for attribute %s from '%s' to '%s'",
-                               data->attribute, attr_vp->strvalue, new_str);
-               attr_vp->length = strlen(new_str);
-               strncpy(attr_vp->strvalue, new_str, (attr_vp->length + 1));
+               DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name,
+                               data->attribute, attr_vp->vp_strvalue, new_str);
+               if (pairparsevalue(attr_vp, new_str) == NULL) {
+                       DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror());
+                       return ret;
+               }
 
 to_do_again:
                ret = RLM_MODULE_OK;
@@ -323,33 +415,10 @@ to_do_again:
                                goto do_again;
                }
        }
-       else{
-               attr_vp = pairmake(data->attribute,replace_STR,0);
-               switch(data->searchin){
-                       case RLM_REGEX_INPACKET:
-                               pairadd(&request->packet->vps,attr_vp);
-                               break;
-                       case RLM_REGEX_INCONFIG:
-                               pairadd(&request->config_items,attr_vp);
-                               break;
-                       case RLM_REGEX_INREPLY:
-                               pairadd(&request->reply->vps,attr_vp);
-                               break;
-                       default:
-                               radlog(L_ERR, "rlm_attr_rewrite: Illegal value for searchin. Changing to packet.");
-                               data->searchin = RLM_REGEX_INPACKET;
-                               pairadd(&request->packet->vps,attr_vp);
-                               break;
-               }
-               DEBUG2("rlm_attr_rewrite: Added attribute %s with value '%s'",data->attribute,attr_vp->strvalue);
-               ret = RLM_MODULE_OK;
-       }
-                               
 
        return ret;
 }
 
-
 static int attr_rewrite_accounting(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
@@ -359,19 +428,23 @@ static int attr_rewrite_authorize(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
 }
+
 static int attr_rewrite_authenticate(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
 }
+
 static int attr_rewrite_preacct(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
 }
-static int attr_rewrite_ismul(void *instance, REQUEST *request)
+
+static int attr_rewrite_checksimul(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
 }
 
+#ifdef WITH_PROXY
 static int attr_rewrite_preproxy(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
@@ -381,6 +454,7 @@ static int attr_rewrite_postproxy(void *instance, REQUEST *request)
 {
        return do_attr_rewrite(instance, request);
 }
+#endif
 
 static int attr_rewrite_postauth(void *instance, REQUEST *request)
 {
@@ -389,17 +463,6 @@ static int attr_rewrite_postauth(void *instance, REQUEST *request)
 
 static int attr_rewrite_detach(void *instance)
 {
-       rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
-
-       if (data->attribute)
-               free(data->attribute);
-       if (data->search)
-               free(data->search);
-       if (data->replace)      
-               free(data->replace);
-       if (data->name)
-               free(data->name);
-
        free(instance);
        return 0;
 }
@@ -414,20 +477,23 @@ static int attr_rewrite_detach(void *instance)
  *     is single-threaded.
  */
 module_t rlm_attr_rewrite = {
-       "attr_rewrite", 
+       RLM_MODULE_INIT,
+       "attr_rewrite",
        RLM_TYPE_THREAD_UNSAFE,         /* type */
-       NULL,                           /* initialization */
        attr_rewrite_instantiate,               /* instantiation */
+       attr_rewrite_detach,                    /* detach */
        {
                attr_rewrite_authenticate,      /* authentication */
                attr_rewrite_authorize,         /* authorization */
                attr_rewrite_preacct,           /* preaccounting */
                attr_rewrite_accounting,        /* accounting */
-               attr_rewrite_ismul,             /* checksimul */
+               attr_rewrite_checksimul,        /* checksimul */
+#ifdef WITH_PROXY
                attr_rewrite_preproxy,          /* pre-proxy */
                attr_rewrite_postproxy,         /* post-proxy */
+#else
+               NULL, NULL,
+#endif
                attr_rewrite_postauth           /* post-auth */
        },
-       attr_rewrite_detach,                    /* detach */
-       NULL,                           /* destroy */
 };