Fix typos in previous commit
[freeradius.git] / src / main / evaluate.c
index 381e1ad..10aeac4 100644 (file)
@@ -162,7 +162,7 @@ static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
 
                        default:
                                if ((p[1] >= '0') && (p[1] <= '9') &&
-                                   (sscanf(p, "%3o", &x) == 1)) {
+                                   (sscanf(p + 1, "%3o", &x) == 1)) {
                                        *q++ = x;
                                        p += 2;
                                } else {
@@ -199,8 +199,7 @@ static const FR_NAME_NUMBER modreturn_table[] = {
 };
 
 
-static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
-                       
+int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
 {
        const char *vp_name = name;
        REQUEST *myrequest = request;
@@ -244,6 +243,42 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
                vp_name += 8;
                vps = myrequest->config_items;
 
+#ifdef WITH_COA
+       } else if (strncmp(vp_name, "coa:", 4) == 0) {
+               vp_name += 4;
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
+                       vps = myrequest->coa->proxy->vps;
+               }
+
+       } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
+               vp_name += 10;
+
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       vps = myrequest->coa->proxy_reply->vps;
+               }
+
+       } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
+               vp_name += 11;
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
+                       vps = myrequest->coa->proxy->vps;
+               }
+
+       } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
+               vp_name += 17;
+
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       vps = myrequest->coa->proxy_reply->vps;
+               }
+#endif
+
        } else {
                vps = myrequest->packet->vps;
        }
@@ -254,7 +289,7 @@ static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
        /*
         *      May not may not be found, but it *is* a known name.
         */
-       *vp_p = pairfind(vps, da->attr);
+       *vp_p = pairfind(vps, da->attr, da->vendor);
        return TRUE;
 }
 
@@ -268,7 +303,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                         int cflags, int modreturn)
 {
        int result;
-       int lint, rint;
+       uint32_t lint, rint;
        VALUE_PAIR *vp = NULL;
 #ifdef HAVE_REGEX_H
        char buffer[1024];
@@ -320,17 +355,19 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                                 *      If so, try looking for it.
                                 */
                                da = dict_attrbyname(pleft);
-                               if (da && radius_find_compare(da->attr)) {
+                               if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
                                        VALUE_PAIR *check = pairmake(pleft, pright, token);
                                        *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
+                                       RDEBUG3("  Callback returns %d",
+                                               *presult);
                                        pairfree(&check);
-                                       if (*presult)  return TRUE;
-                                       return FALSE;
+                                       return TRUE;
                                }
                                
                                RDEBUG2("    (Attribute %s was not found)",
                                       pleft);
-                               return FALSE;
+                               *presult = 0;
+                               return TRUE;
                        }
 
 #ifdef HAVE_REGEX_H
@@ -355,6 +392,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
 
                        myvp.operator = token;
                        *presult = paircmp(&myvp, vp);
+                       RDEBUG3("  paircmp -> %d", *presult);
                        return TRUE;
                } /* else it's not a VP in a list */
        }
@@ -371,12 +409,12 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                        RDEBUG2("    (Right field is not a number at: %s)", pright);
                        return FALSE;
                }
-               rint = atoi(pright);
+               rint = strtoul(pright, NULL, 0);
                if (!all_digits(pleft)) {
                        RDEBUG2("    (Left field is not a number at: %s)", pleft);
                        return FALSE;
                }
-               lint = atoi(pleft);
+               lint = strtoul(pleft, NULL, 0);
                break;
                
        default:
@@ -390,7 +428,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
                 *      Check for truth or falsehood.
                 */
                if (all_digits(pleft)) {
-                       lint = atoi(pleft);
+                       lint = strtoul(pleft, NULL, 0);
                        result = (lint != 0);
                        
                } else {
@@ -518,6 +556,7 @@ static int radius_do_cmp(REQUEST *request, int *presult,
        return TRUE;
 }
 
+
 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                              const char **ptr, int evaluate_it, int *presult)
 {
@@ -545,14 +584,25 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
        while (*p) {
                while ((*p == ' ') || (*p == '\t')) p++;
 
+               /*
+                *      ! EXPR
+                */
                if (!found_condition && (*p == '!')) {
-                       RDEBUG4(">>> INVERT");
-                       invert = TRUE;
+                       /*
+                        *      Don't change the results if we're not
+                        *      evaluating the condition.
+                        */
+                       if (evaluate_next_condition) {
+                               RDEBUG4(">>> INVERT");
+                               invert = TRUE;
+                       }
                        p++;
+
+                       while ((*p == ' ') || (*p == '\t')) p++;
                }
 
                /*
-                *      It's a subcondition.
+                *      ( EXPR ) 
                 */
                if (!found_condition && (*p == '(')) {
                        const char *end = p + 1;
@@ -570,13 +620,13 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                        }
 
                        if (invert) {
-                               if (evaluate_next_condition)
-                               RDEBUG2("%.*s Converting !%s -> %s",
-                                      depth, filler,
-                                      (result != FALSE) ? "TRUE" : "FALSE",
-                                      (result == FALSE) ? "TRUE" : "FALSE");
-
-                               result = (result == FALSE);
+                               if (evaluate_next_condition) {
+                                       RDEBUG2("%.*s Converting !%s -> %s",
+                                               depth, filler,
+                                               (result != FALSE) ? "TRUE" : "FALSE",
+                                               (result == FALSE) ? "TRUE" : "FALSE");
+                                       result = (result == FALSE);
+                               }
                                invert = FALSE;
                        }
 
@@ -596,9 +646,9 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
 
                        if (*p == ')') p++; /* eat closing brace */
                        found_condition = TRUE;
-               }
 
-               while ((*p == ' ') || (*p == '\t')) p++;
+                       while ((*p == ' ') || (*p == '\t')) p++;
+               }
 
                /*
                 *      At EOL or closing brace, update && return.
@@ -610,8 +660,8 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                 *
                 *      WORD
                 *      WORD1 op WORD2
-                *      && condition
-                *      || condition
+                *      && EXPR
+                *      || EXPR
                 */
                if (found_condition) {
                        /*
@@ -692,8 +742,8 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                 *
                 *      WORD1 op WORD2
                 *      WORD )
-                *      WORD && condition
-                *      WORD || condition
+                *      WORD && EXPR
+                *      WORD || EXPR
                 */
                q = p;
                while ((*q == ' ') || (*q == '\t')) q++;
@@ -725,8 +775,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                 */
                token = gettoken(&p, comp, sizeof(comp));
                if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
-                   (token == T_OP_CMP_TRUE) ||
-                   (token == T_OP_CMP_FALSE)) {
+                   (token == T_OP_CMP_TRUE)) {
                        radlog(L_ERR, "Expected comparison at: %s", comp);
                        return FALSE;
                }
@@ -785,6 +834,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                                           rt, pright, cflags, modreturn)) {
                                return FALSE;
                        }
+                       RDEBUG4(">>> Comparison returned %d", result);
 
                        if (invert) {
                                RDEBUG4(">>> INVERTING result");
@@ -812,6 +862,11 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
                found_condition = TRUE;
        } /* loop over the input condition */
 
+       if (!found_condition) {
+               radlog(L_ERR, "Syntax error.  Expected condition at %s", p);
+               return FALSE;
+       }
+
        RDEBUG4(">>> AT EOL -> %d", result);
        *ptr = p;
        if (evaluate_it) *presult = result;
@@ -827,6 +882,8 @@ static void fix_up(REQUEST *request)
        request->password = NULL;
        
        for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
+               if (vp->vendor != 0) continue;
+
                if ((vp->attribute == PW_USER_NAME) &&
                    !request->username) {
                        request->username = vp;
@@ -837,9 +894,12 @@ static void fix_up(REQUEST *request)
                } else if (vp->attribute == PW_USER_PASSWORD) {
                        request->password = vp;
                }
+
+               if (request->username && request->password) break;
        }
 }
 
+
 /*
  *     The pairmove() function in src/lib/valuepair.c does all sorts of
  *     extra magic that we don't want here.
@@ -924,12 +984,13 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 
                found = FALSE;
                for (j = 0; j < to_count; j++) {
-                       if (edited[j]) continue;
+                       if (edited[j] || !to_list[j] || !from_list[i]) continue;
 
                        /*
                         *      Attributes aren't the same, skip them.
                         */
-                       if (from_list[i]->attribute != to_list[j]->attribute) {
+                       if ((from_list[i]->attribute != to_list[j]->attribute) ||
+                           (from_list[i]->vendor != to_list[j]->vendor)) {
                                continue;
                        }
 
@@ -966,6 +1027,14 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                        }
 
                        /*
+                        *      Delete every attribute, independent
+                        *      of its value.
+                        */
+                       if (from_list[i]->operator == T_OP_CMP_FALSE) {
+                               goto delete;
+                       }
+
+                       /*
                         *      Delete all matching attributes from
                         *      "to"
                         */
@@ -1099,6 +1168,9 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
                last = &(*last)->next;
        }
 
+       rad_assert(request != NULL);
+       rad_assert(request->packet != NULL);
+
        /*
         *      Fix dumb cache issues
         */
@@ -1149,7 +1221,7 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
        CONF_ITEM *ci;
        VALUE_PAIR *newlist, *vp;
        VALUE_PAIR **output_vps = NULL;
-       REQUEST *request_vps = request;
+       REQUEST *myrequest = request;
 
        if (!request || !cs) return RLM_MODULE_INVALID;
 
@@ -1160,29 +1232,67 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
        if (strncmp(name, "outer.", 6) == 0) {
                if (!request->parent) return RLM_MODULE_NOOP;
 
-               request_vps = request->parent;
+               myrequest = request->parent;
                name += 6;
        }
 
        if (strcmp(name, "request") == 0) {
-               output_vps = &request_vps->packet->vps;
+               output_vps = &myrequest->packet->vps;
 
        } else if (strcmp(name, "reply") == 0) {
-               output_vps = &request_vps->reply->vps;
+               output_vps = &myrequest->reply->vps;
 
 #ifdef WITH_PROXY
        } else if (strcmp(name, "proxy-request") == 0) {
-               if (request->proxy) output_vps = &request_vps->proxy->vps;
+               if (request->proxy) output_vps = &myrequest->proxy->vps;
 
        } else if (strcmp(name, "proxy-reply") == 0) {
                if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
 #endif
 
        } else if (strcmp(name, "config") == 0) {
-               output_vps = &request_vps->config_items;
+               output_vps = &myrequest->config_items;
 
        } else if (strcmp(name, "control") == 0) {
-               output_vps = &request_vps->config_items;
+               output_vps = &myrequest->config_items;
+
+#ifdef WITH_COA
+       } else if (strcmp(name, "coa") == 0) {
+               if (!myrequest->coa) {
+                       request_alloc_coa(myrequest);
+                       myrequest->coa->proxy->code = PW_COA_REQUEST;
+               }
+                 
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
+                       output_vps = &myrequest->coa->proxy->vps;
+               }
+
+       } else if (strcmp(name, "coa-reply") == 0) {
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
+                    (myrequest->coa->proxy_reply)) {
+                     output_vps = &myrequest->coa->proxy_reply->vps;
+               }
+
+       } else if (strcmp(name, "disconnect") == 0) {
+               if (!myrequest->coa) {
+                       request_alloc_coa(myrequest);
+                       if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
+               }
+
+               if (myrequest->coa &&
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
+                       output_vps = &myrequest->coa->proxy->vps;
+               }
+
+       } else if (strcmp(name, "disconnect-reply") == 0) {
+               if (myrequest->coa && /* match reply with request */
+                   (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
+                   (myrequest->coa->proxy_reply)) {
+                       output_vps = &myrequest->coa->proxy_reply->vps;
+               }
+#endif
 
        } else {
                return RLM_MODULE_INVALID;
@@ -1212,6 +1322,13 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
 
                cp = cf_itemtopair(ci);
 
+#ifndef NDEBUG
+               if (debug_flag && (vp->vendor == 0) &&
+                   radius_find_compare(vp->attribute)) {
+                       DEBUG("WARNING: You are modifying the value of virtual attribute %s.  This is not supported.", vp->name);
+               }
+#endif
+
                /*
                 *      The VP && CF lists should be in sync.  If they're
                 *      not, panic.