Signed / unsigned fixes and function prototypes
[freeradius.git] / src / main / valuepair.c
index 33c1257..50f4cb6 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 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  * Copyright 2000  Alan DeKok <aland@ox.org>
  */
 
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include <freeradius-devel/autoconf.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_NETINET_IN_H
-#      include <netinet/in.h>
-#endif
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
 
 #ifdef HAVE_REGEX_H
 #      include <regex.h>
@@ -51,47 +45,150 @@ static const char rcsid[] = "$Id$";
 #endif
 #endif
 
-#include <freeradius-devel/radiusd.h>
-
 struct cmp {
-       int attribute;
-       int otherattr;
+       unsigned int attribute;
+       unsigned int otherattr;
        void *instance; /* module instance */
        RAD_COMPARE_FUNC compare;
        struct cmp *next;
 };
 static struct cmp *cmp;
 
-
-/*
- *     Compare 2 attributes. May call the attribute compare function.
- */
-static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
-                      VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
 {
        int ret = -2;
-       struct cmp *c;
 
        /*
         *      Check for =* and !* and return appropriately
         */
        if( check->operator == T_OP_CMP_TRUE )
-                return 0;  /* always return 0/EQUAL */
+                return 0;
        if( check->operator == T_OP_CMP_FALSE )
-                return 1;  /* always return 1/NOT EQUAL */
+                return 1;
+
+#ifdef HAVE_REGEX_H
+       if (check->operator == T_OP_REG_EQ) {
+               int i, compare;
+               regex_t reg;
+               char name[1024];
+               char value[1024];
+               regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
+
+               snprintf(name, sizeof(name), "%%{%s}", check->name);
+               radius_xlat(value, sizeof(value), name, request, NULL);
+
+               /*
+                *      Include substring matches.
+                */
+               compare = regcomp(&reg, check->vp_strvalue, REG_EXTENDED);
+               if (compare != 0) {
+                       char buffer[256];
+                       regerror(compare, &reg, buffer, sizeof(buffer));
+
+                       RDEBUG("Invalid regular expression %s: %s",
+                              check->vp_strvalue, buffer);
+                       return -1;
+               }
+               compare = regexec(&reg, value,  REQUEST_MAX_REGEX + 1,
+                                 rxmatch, 0);
+               regfree(&reg);
+
+               /*
+                *      Add %{0}, %{1}, etc.
+                */
+               for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+                       char *p;
+                       char buffer[sizeof(check->vp_strvalue)];
+
+                       /*
+                        *      Didn't match: delete old
+                        *      match, if it existed.
+                        */
+                       if ((compare != 0) ||
+                           (rxmatch[i].rm_so == -1)) {
+                               p = request_data_get(request, request,
+                                                    REQUEST_DATA_REGEX | i);
+                               if (p) {
+                                       free(p);
+                                       continue;
+                               }
+
+                               /*
+                                *      No previous match
+                                *      to delete, stop.
+                                */
+                               break;
+                       }
+
+                       /*
+                        *      Copy substring into buffer.
+                        */
+                       memcpy(buffer, value + rxmatch[i].rm_so,
+                              rxmatch[i].rm_eo - rxmatch[i].rm_so);
+                       buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
+
+                       /*
+                        *      Copy substring, and add it to
+                        *      the request.
+                        *
+                        *      Note that we don't check
+                        *      for out of memory, which is
+                        *      the only error we can get...
+                        */
+                       p = strdup(buffer);
+                       request_data_add(request, request,
+                                        REQUEST_DATA_REGEX | i,
+                                        p, free);
+               }
+               if (compare == 0) return 0;
+               return -1;
+       }
+
+       if (check->operator == T_OP_REG_NE) {
+               int compare;
+               regex_t reg;
+               char name[1024];
+               char value[1024];
+               regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
+
+               snprintf(name, sizeof(name), "%%{%s}", check->name);
+               radius_xlat(value, sizeof(value), name, request, NULL);
+
+               /*
+                *      Include substring matches.
+                */
+               compare = regcomp(&reg, (char *)check->vp_strvalue,
+                                 REG_EXTENDED);
+               if (compare != 0) {
+                       char buffer[256];
+                       regerror(compare, &reg, buffer, sizeof(buffer));
+
+                       RDEBUG("Invalid regular expression %s: %s",
+                              check->vp_strvalue, buffer);
+                       return -1;
+               }
+               compare = regexec(&reg, value,  REQUEST_MAX_REGEX + 1,
+                                 rxmatch, 0);
+               regfree(&reg);
+
+               if (compare != 0) return 0;
+               return -1;
+
+       }
+#endif
 
        /*
-        *      See if there is a special compare function.
-        *
-        *      FIXME: use new RB-Tree code.
+        *      Tagged attributes are equal if and only if both the
+        *      tag AND value match.
         */
-       for (c = cmp; c; c = c->next)
-               if (c->attribute == check->attribute)
-                       return (c->compare)(c->instance, req, request, check,
-                               check_pairs, reply_pairs);
-
-       if (!request) return -1; /* doesn't exist, don't compare it */
+       if (check->flags.has_tag) {
+               ret = ((int) vp->flags.tag) - ((int) check->flags.tag);
+               if (ret != 0) return ret;
+       }
 
+       /*
+        *      Not a regular expression, compare the types.
+        */
        switch(check->type) {
 #ifdef ASCEND_BINARY
                /*
@@ -101,42 +198,41 @@ static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
                case PW_TYPE_ABINARY:
 #endif
                case PW_TYPE_OCTETS:
-                       if (request->length != check->length) {
+                       if (vp->length != check->length) {
                                ret = 1; /* NOT equal */
                                break;
                        }
-                       ret = memcmp(request->vp_strvalue, check->vp_strvalue,
-                                       request->length);
+                       ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
+                                    vp->length);
                        break;
                case PW_TYPE_STRING:
-                       if (check->flags.caseless) {
-                               ret = strcasecmp((char *)request->vp_strvalue,
-                                                (char *)check->vp_strvalue);
-                       } else {
-                               ret = strcmp((char *)request->vp_strvalue,
-                                            (char *)check->vp_strvalue);
-                       }
+                       ret = strcmp((char *)vp->vp_strvalue,
+                                    (char *)check->vp_strvalue);
                        break;
+               case PW_TYPE_BYTE:
+               case PW_TYPE_SHORT:
                case PW_TYPE_INTEGER:
+                       ret = vp->vp_integer - check->vp_integer;
+                       break;
                case PW_TYPE_DATE:
-                       ret = request->lvalue - check->lvalue;
+                       ret = vp->vp_date - check->vp_date;
                        break;
                case PW_TYPE_IPADDR:
-                       ret = ntohl(request->vp_ipaddr) - ntohl(check->vp_ipaddr);
+                       ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
                        break;
                case PW_TYPE_IPV6ADDR:
-                       ret = memcmp(&request->vp_ipv6addr, &check->vp_ipv6addr,
-                                    sizeof(request->vp_ipv6addr));
+                       ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
+                                    sizeof(vp->vp_ipv6addr));
                        break;
-                       
+
                case PW_TYPE_IPV6PREFIX:
-                       ret = memcmp(&request->vp_ipv6prefix, &check->vp_ipv6prefix,
-                                    sizeof(request->vp_ipv6prefix));
+                       ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
+                                    sizeof(vp->vp_ipv6prefix));
                        break;
-               
+
                case PW_TYPE_IFID:
-                       ret = memcmp(&request->vp_ifid, &check->vp_ifid,
-                                    sizeof(request->vp_ifid));
+                       ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
+                                    sizeof(vp->vp_ifid));
                        break;
 
                default:
@@ -148,9 +244,61 @@ static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
 
 
 /*
+ *     Compare 2 attributes. May call the attribute compare function.
+ */
+int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
+                           VALUE_PAIR *check, VALUE_PAIR *check_pairs,
+                           VALUE_PAIR **reply_pairs)
+{
+       struct cmp *c;
+
+       /*
+        *      Check for =* and !* and return appropriately
+        */
+       if( check->operator == T_OP_CMP_TRUE )
+                return 0;  /* always return 0/EQUAL */
+       if( check->operator == T_OP_CMP_FALSE )
+                return 1;  /* always return 1/NOT EQUAL */
+
+       /*
+        *      See if there is a special compare function.
+        *
+        *      FIXME: use new RB-Tree code.
+        */
+       for (c = cmp; c; c = c->next)
+               if ((c->attribute == check->attribute) &&
+                   (check->vendor == 0)) {
+                       return (c->compare)(c->instance, req, request, check,
+                               check_pairs, reply_pairs);
+               }
+
+       if (!request) return -1; /* doesn't exist, don't compare it */
+
+       return radius_compare_vps(req, check, request);
+}
+
+
+/*
+ *     Find a comparison function for two attributes.
+ */
+int radius_find_compare(unsigned int attribute)
+{
+       struct cmp *c;
+
+       for (c = cmp; c; c = c->next) {
+               if (c->attribute == attribute) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+
+/*
  *     See what attribute we want to compare with.
  */
-static int otherattr(int attr)
+static int otherattr(unsigned int attr)
 {
        struct cmp      *c;
 
@@ -175,7 +323,7 @@ static int otherattr(int attr)
  *     For example, PW_GROUP in a check item needs to be compared
  *     with PW_USER_NAME in the incoming request.
  */
-int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
+int paircompare_register(unsigned int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
 {
        struct cmp      *c;
 
@@ -183,8 +331,6 @@ int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void
 
        c = rad_malloc(sizeof(struct cmp));
 
-       if (compare_attr < 0)
-               compare_attr = attr;
        c->compare = fun;
        c->attribute = attr;
        c->otherattr = compare_attr;
@@ -198,7 +344,7 @@ int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void
 /*
  *     Unregister a function.
  */
-void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
+void paircompare_unregister(unsigned int attr, RAD_COMPARE_FUNC fun)
 {
        struct cmp      *c, *last;
 
@@ -233,9 +379,6 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
        int result = 0;
        int compare;
        int other;
-#ifdef HAVE_REGEX_H
-       regex_t reg;
-#endif
 
        for (check_item = check; check_item != NULL; check_item = check_item->next) {
                /*
@@ -271,8 +414,13 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
                         *
                         *      This hack makes CHAP-Password work..
                         */
-                       case PW_PASSWORD:
-                               if (pairfind(request, PW_PASSWORD) == NULL) {
+                       case PW_USER_PASSWORD:
+                               if (check_item->operator == T_OP_CMP_EQ) {
+                                       DEBUG("WARNING: Found User-Password == \"...\".");
+                                       DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
+                                       DEBUG("WARNING: See \"man rlm_pap\" for more information.");
+                               }
+                               if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) {
                                        continue;
                                }
                                break;
@@ -285,9 +433,11 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
 
                auth_item = request;
        try_again:
-               for (; auth_item != NULL; auth_item = auth_item->next) {
-                       if (auth_item->attribute == other || other == 0)
-                               break;
+               if (other >= 0) {
+                       for (; auth_item != NULL; auth_item = auth_item->next) {
+                         if (auth_item->attribute == (unsigned int) other || other == 0)
+                                       break;
+                       }
                }
 
                /*
@@ -299,7 +449,7 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
                         *      to not find it, then we succeeded.
                         */
                        if (check_item->operator == T_OP_CMP_FALSE)
-                               return 0;
+                               continue;
                        else
                                return -1;
                }
@@ -334,7 +484,8 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
                /*
                 *      OK it is present now compare them.
                 */
-               compare = compare_pair(req, auth_item, check_item, check, reply);
+               compare = radius_callback_compare(req, auth_item, check_item,
+                                                 check, reply);
 
                switch (check_item->operator) {
                        case T_OP_EQ:
@@ -370,104 +521,17 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
 
 #ifdef HAVE_REGEX_H
                        case T_OP_REG_EQ:
-                       {
-                               int i;
-                               regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
-
-                               if ((auth_item->type == PW_TYPE_IPADDR) &&
-                                   (auth_item->vp_strvalue[0] == '\0')) {
-                                 inet_ntop(AF_INET, &(auth_item->lvalue),
-                                           auth_item->vp_strvalue,
-                                           sizeof(auth_item->vp_strvalue));
-                               }
-
-                               /*
-                                *      Include substring matches.
-                                */
-                               regcomp(&reg, (char *)check_item->vp_strvalue,
-                                       REG_EXTENDED);
-                               compare = regexec(&reg,
-                                                 (char *)auth_item->vp_strvalue,
-                                                 REQUEST_MAX_REGEX + 1,
-                                                 rxmatch, 0);
-                               regfree(&reg);
-
-                               /*
-                                *      Add %{0}, %{1}, etc.
-                                */
-                               for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
-                                       char *p;
-                                       char buffer[sizeof(check_item->vp_strvalue)];
-
-                                       /*
-                                        *      Didn't match: delete old
-                                        *      match, if it existed.
-                                        */
-                                       if ((compare != 0) ||
-                                           (rxmatch[i].rm_so == -1)) {
-                                               p = request_data_get(req, req,
-                                                                    REQUEST_DATA_REGEX | i);
-                                               if (p) {
-                                                       free(p);
-                                                       continue;
-                                               }
-
-                                               /*
-                                                *      No previous match
-                                                *      to delete, stop.
-                                                */
-                                               break;
-                                       }
-                                       
-                                       /*
-                                        *      Copy substring into buffer.
-                                        */
-                                       memcpy(buffer,
-                                              auth_item->vp_strvalue + rxmatch[i].rm_so,
-                                              rxmatch[i].rm_eo - rxmatch[i].rm_so);
-                                       buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
-
-                                       /*
-                                        *      Copy substring, and add it to
-                                        *      the request.
-                                        *
-                                        *      Note that we don't check
-                                        *      for out of memory, which is
-                                        *      the only error we can get...
-                                        */
-                                       p = strdup(buffer);
-                                       request_data_add(req,
-                                                        req,
-                                                        REQUEST_DATA_REGEX | i,
-                                                        p, free);
-                               }
-                       }                               
-                               if (compare != 0) result = -1;
-                               break;
-
                        case T_OP_REG_NE:
-                               if ((auth_item->type == PW_TYPE_IPADDR) &&
-                                   (auth_item->vp_strvalue[0] == '\0')) {
-                                 inet_ntop(AF_INET, &(auth_item->lvalue),
-                                           auth_item->vp_strvalue,
-                                           sizeof(auth_item->vp_strvalue));
-                               }
-
-                               regcomp(&reg, (char *)check_item->vp_strvalue, REG_EXTENDED|REG_NOSUB);
-                               compare = regexec(&reg, (char *)auth_item->vp_strvalue,
-                                               0, NULL, 0);
-                               regfree(&reg);
-                               if (compare == 0) result = -1;
+                               result = compare;
                                break;
 #endif
-
                } /* switch over the operator of the check item */
 
                /*
                 *      This attribute didn't match, but maybe there's
                 *      another of the same attribute, which DOES match.
                 */
-               if (result != 0) {
+               if ((result != 0) && (other >= 0)) {
                        auth_item = auth_item->next;
                        result = 0;
                        goto try_again;
@@ -475,19 +539,9 @@ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR
 
        } /* for every entry in the check item list */
 
-       return 0;               /* it matched */
-}
-
-/*
- *      Compare two attributes simply.  Calls compare_pair.
- */
-
-int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
-{
-       return compare_pair( req, first, second, NULL, NULL );
+       return result;
 }
 
-
 /*
  *     Move pairs, replacing/over-writing them, and doing xlat.
  */
@@ -541,7 +595,7 @@ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
                        pairparsevalue(i, buffer);
                }
 
-               found = pairfind(*to, i->attribute);
+               found = pairfind(*to, i->attribute, i->vendor);
                switch (i->operator) {
 
                        /*
@@ -553,7 +607,7 @@ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
                                if (!i->vp_strvalue[0] ||
                                    (strcmp((char *)found->vp_strvalue,
                                            (char *)i->vp_strvalue) == 0)){
-                                       pairdelete(to, found->attribute);
+                                 pairdelete(to, found->attribute, found->vendor);
 
                                        /*
                                         *      'tailto' may have been
@@ -591,6 +645,7 @@ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
                                vp = found->next;
                                memcpy(found, i, sizeof(*found));
                                found->next = vp;
+                               tailfrom = i;
                                continue;
                        }
                        break;
@@ -631,3 +686,72 @@ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
                }
        } /* loop over the 'from' list */
 }
+
+/*
+ *     Create a pair, and add it to a particular list of VPs
+ *
+ *     Note that this function ALWAYS returns.  If we're OOM, then
+ *     it causes the server to exit!
+ */
+VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
+                             unsigned int attribute, unsigned int vendor, int type)
+{
+       VALUE_PAIR *vp;
+
+       request = request;      /* -Wunused */
+
+       vp = paircreate(attribute, vendor, type);
+       if (!vp) {
+               radlog(L_ERR, "No memory!");
+               rad_assert("No memory" == NULL);
+               _exit(1);
+       }
+
+       if (vps) pairadd(vps, vp);
+
+       return vp;
+}
+
+/*
+ *     Create a pair, and add it to a particular list of VPs
+ *
+ *     Note that this function ALWAYS returns.  If we're OOM, then
+ *     it causes the server to exit!
+ */
+VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
+                           const char *attribute, const char *value,
+                           int operator)
+{
+       VALUE_PAIR *vp;
+
+       request = request;      /* -Wunused */
+
+       vp = pairmake(attribute, value, operator);
+       if (!vp) return NULL;
+
+       if (vps) pairadd(vps, vp);
+
+       return vp;
+}
+
+void debug_pair(VALUE_PAIR *vp)
+{
+       if (!vp || !debug_flag || !fr_log_fp) return;
+
+       fputc('\t', fr_log_fp);
+       vp_print(fr_log_fp, vp);
+       fputc('\n', fr_log_fp);
+}
+
+void debug_pair_list(VALUE_PAIR *vp)
+{
+       if (!vp || !debug_flag || !fr_log_fp) return;
+
+       while (vp) {
+               fputc('\t', fr_log_fp);
+               vp_print(fr_log_fp, vp);
+               fputc('\n', fr_log_fp);
+               vp = vp->next;
+       }
+       fflush(fr_log_fp);
+}