X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Fevaluate.c;h=365e463e7d475f7e7a88849cbd87b6f9554f383b;hb=e5e00e20f111a784dc40665147fd44097a208741;hp=19d387a37691aa7159c68e86baf932d3142f13d2;hpb=f6e312acefc868cc8ea0c3b5876f04812dde4b36;p=freeradius.git diff --git a/src/main/evaluate.c b/src/main/evaluate.c index 19d387a..365e463 100644 --- a/src/main/evaluate.c +++ b/src/main/evaluate.c @@ -47,6 +47,7 @@ RCSID("$Id$") #endif #endif +#ifdef WITH_UNLANG static int all_digits(const char *string) { @@ -59,19 +60,11 @@ static int all_digits(const char *string) return (*p == '\0'); } -#ifndef NDEBUG -#ifndef DEBUG4 -#define DEBUG4 if (debug_flag > 4)log_debug -#endif -#else -#define DEBUG4 if (0) log_debug -#endif - static const char *filler = "????????????????????????????????????????????????????????????????"; static const char *expand_string(char *buffer, size_t sizeof_buffer, REQUEST *request, - LRAD_TOKEN value_type, const char *value) + FR_TOKEN value_type, const char *value) { int result; char *p; @@ -101,7 +94,6 @@ static const char *expand_string(char *buffer, size_t sizeof_buffer, } return buffer; - case T_DOUBLE_QUOTED_STRING: if (!strchr(value, '%')) return value; @@ -113,9 +105,10 @@ static const char *expand_string(char *buffer, size_t sizeof_buffer, } #ifdef HAVE_REGEX_H -static LRAD_TOKEN getregex(char **ptr, char *buffer, size_t buflen, int *pcflags) +static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen, + int *pcflags) { - char *p = *ptr; + const char *p = *ptr; char *q = buffer; if (*p != '/') return T_OP_INVALID; @@ -169,7 +162,7 @@ static LRAD_TOKEN getregex(char **ptr, char *buffer, size_t buflen, int *pcflags default: if ((p[1] >= '0') && (p[1] <= '9') && - (sscanf(p, "%3o", &x) == 1)) { + (sscanf(p + 1, "%3o", &x) == 1)) { *q++ = x; p += 2; } else { @@ -188,11 +181,11 @@ static LRAD_TOKEN getregex(char **ptr, char *buffer, size_t buflen, int *pcflags *q = '\0'; *ptr = p; - return T_BARE_WORD; + return T_DOUBLE_QUOTED_STRING; } #endif -static const LRAD_NAME_NUMBER modreturn_table[] = { +static const FR_NAME_NUMBER modreturn_table[] = { { "reject", RLM_MODULE_REJECT }, { "fail", RLM_MODULE_FAIL }, { "ok", RLM_MODULE_OK }, @@ -205,6 +198,365 @@ static const LRAD_NAME_NUMBER modreturn_table[] = { { NULL, 0 } }; + +int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p) +{ + const char *vp_name = name; + REQUEST *myrequest = request; + DICT_ATTR *da; + VALUE_PAIR *vps = NULL; + + *vp_p = NULL; + + /* + * Allow for tunneled sessions. + */ + if (strncmp(vp_name, "outer.", 6) == 0) { + if (!myrequest->parent) return TRUE; + vp_name += 6; + myrequest = myrequest->parent; + } + + if (strncmp(vp_name, "request:", 8) == 0) { + vp_name += 8; + vps = myrequest->packet->vps; + + } else if (strncmp(vp_name, "reply:", 6) == 0) { + vp_name += 6; + vps = myrequest->reply->vps; + +#ifdef WITH_PROXY + } else if (strncmp(vp_name, "proxy-request:", 14) == 0) { + vp_name += 14; + if (request->proxy) vps = myrequest->proxy->vps; + + } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) { + vp_name += 12; + if (request->proxy_reply) vps = myrequest->proxy_reply->vps; +#endif + + } else if (strncmp(vp_name, "config:", 7) == 0) { + vp_name += 7; + vps = myrequest->config_items; + + } else if (strncmp(vp_name, "control:", 8) == 0) { + 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; + } + + da = dict_attrbyname(vp_name); + if (!da) return FALSE; /* not a dictionary name */ + + /* + * May not may not be found, but it *is* a known name. + */ + *vp_p = pairfind(vps, da->attr, da->vendor); + return TRUE; +} + + +/* + * *presult is "did comparison match or not" + */ +static int radius_do_cmp(REQUEST *request, int *presult, + FR_TOKEN lt, const char *pleft, FR_TOKEN token, + FR_TOKEN rt, const char *pright, + int cflags, int modreturn) +{ + int result; + uint32_t lint, rint; + VALUE_PAIR *vp = NULL; +#ifdef HAVE_REGEX_H + char buffer[1024]; +#else + cflags = cflags; /* -Wunused */ +#endif + + rt = rt; /* -Wunused */ + + if (lt == T_BARE_WORD) { + /* + * Maybe check the last return code. + */ + if (token == T_OP_CMP_TRUE) { + int isreturn; + + /* + * Looks like a return code, treat is as such. + */ + isreturn = fr_str2int(modreturn_table, pleft, -1); + if (isreturn != -1) { + *presult = (modreturn == isreturn); + return TRUE; + } + } + + /* + * Bare words on the left can be attribute names. + */ + if (radius_get_vp(request, pleft, &vp)) { + VALUE_PAIR myvp; + + /* + * VP exists, and that's all we're looking for. + */ + if (token == T_OP_CMP_TRUE) { + *presult = (vp != NULL); + return TRUE; + } + + if (!vp) { + DICT_ATTR *da; + + /* + * The attribute on the LHS may + * have been a dynamically + * registered callback. i.e. it + * doesn't exist as a VALUE_PAIR. + * If so, try looking for it. + */ + da = dict_attrbyname(pleft); + 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); + return TRUE; + } + + RDEBUG2(" (Attribute %s was not found)", + pleft); + *presult = 0; + return TRUE; + } + +#ifdef HAVE_REGEX_H + /* + * Regex comparisons treat everything as + * strings. + */ + if ((token == T_OP_REG_EQ) || + (token == T_OP_REG_NE)) { + vp_prints_value(buffer, sizeof(buffer), vp, 0); + pleft = buffer; + goto do_checks; + } +#endif + + memcpy(&myvp, vp, sizeof(myvp)); + if (!pairparsevalue(&myvp, pright)) { + RDEBUG2("Failed parsing \"%s\": %s", + pright, fr_strerror()); + return FALSE; + } + + myvp.operator = token; + *presult = paircmp(&myvp, vp); + RDEBUG3(" paircmp -> %d", *presult); + return TRUE; + } /* else it's not a VP in a list */ + } + +#ifdef HAVE_REGEX_H + do_checks: +#endif + switch (token) { + case T_OP_GE: + case T_OP_GT: + case T_OP_LE: + case T_OP_LT: + if (!all_digits(pright)) { + RDEBUG2(" (Right field is not a number at: %s)", pright); + return FALSE; + } + rint = strtoul(pright, NULL, 0); + if (!all_digits(pleft)) { + RDEBUG2(" (Left field is not a number at: %s)", pleft); + return FALSE; + } + lint = strtoul(pleft, NULL, 0); + break; + + default: + lint = rint = 0; /* quiet the compiler */ + break; + } + + switch (token) { + case T_OP_CMP_TRUE: + /* + * Check for truth or falsehood. + */ + if (all_digits(pleft)) { + lint = strtoul(pleft, NULL, 0); + result = (lint != 0); + + } else { + result = (*pleft != '\0'); + } + break; + + + case T_OP_CMP_EQ: + result = (strcmp(pleft, pright) == 0); + break; + + case T_OP_NE: + result = (strcmp(pleft, pright) != 0); + break; + + case T_OP_GE: + result = (lint >= rint); + break; + + case T_OP_GT: + result = (lint > rint); + break; + + case T_OP_LE: + result = (lint <= rint); + break; + + case T_OP_LT: + result = (lint < rint); + break; + +#ifdef HAVE_REGEX_H + case T_OP_REG_EQ: { + int i, compare; + regex_t reg; + regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; + + /* + * Include substring matches. + */ + compare = regcomp(®, pright, cflags); + if (compare != 0) { + if (debug_flag) { + char errbuf[128]; + + regerror(compare, ®, errbuf, sizeof(errbuf)); + DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); + } + return FALSE; + } + + compare = regexec(®, pleft, + REQUEST_MAX_REGEX + 1, + rxmatch, 0); + regfree(®); + + /* + * Add new %{0}, %{1}, etc. + */ + if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { + char *r; + + free(request_data_get(request, request, + REQUEST_DATA_REGEX | i)); + + /* + * No %{i}, skip it. + * We MAY have %{2} without %{1}. + */ + if (rxmatch[i].rm_so == -1) continue; + + /* + * Copy substring into allocated buffer + */ + r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); + memcpy(r, pleft + rxmatch[i].rm_so, + rxmatch[i].rm_eo - rxmatch[i].rm_so); + r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; + + request_data_add(request, request, + REQUEST_DATA_REGEX | i, + r, free); + } + result = (compare == 0); + } + break; + + case T_OP_REG_NE: { + int compare; + regex_t reg; + regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; + + /* + * Include substring matches. + */ + compare = regcomp(®, pright, cflags); + if (compare != 0) { + if (debug_flag) { + char errbuf[128]; + + regerror(compare, ®, errbuf, sizeof(errbuf)); + DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); + } + return FALSE; + } + + compare = regexec(®, pleft, + REQUEST_MAX_REGEX + 1, + rxmatch, 0); + regfree(®); + + result = (compare != 0); + } + break; +#endif + + default: + RDEBUG4(">>> NOT IMPLEMENTED %d", token); + result = FALSE; + break; + } + + *presult = result; + return TRUE; +} + + int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, const char **ptr, int evaluate_it, int *presult) { @@ -212,42 +564,54 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, int result = TRUE; int invert = FALSE; int evaluate_next_condition = evaluate_it; - const char *p = *ptr; + const char *p; const char *q, *start; - LRAD_TOKEN token, lt, rt; + FR_TOKEN token, lt, rt; char left[1024], right[1024], comp[4]; const char *pleft, *pright; char xleft[1024], xright[1024]; - int lint, rint; -#ifdef HAVE_REGEX_H int cflags = 0; -#endif - + if (!ptr || !*ptr || (depth >= 64)) { radlog(L_ERR, "Internal sanity check failed in evaluate condition"); return FALSE; } + /* + * Horrible parser. + */ + p = *ptr; while (*p) { while ((*p == ' ') || (*p == '\t')) p++; - if (*p == '!') { - DEBUG4(">>> INVERT"); - invert = TRUE; + /* + * ! EXPR + */ + if (!found_condition && (*p == '!')) { + /* + * 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 (*p == '(') { + if (!found_condition && (*p == '(')) { const char *end = p + 1; /* * Evaluate the condition, bailing out on * parse error. */ - DEBUG4(">>> CALLING EVALUATE %s", end); + RDEBUG4(">>> RECURSING WITH ... %s", end); if (!radius_evaluate_condition(request, modreturn, depth + 1, &end, evaluate_next_condition, @@ -255,14 +619,14 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, return FALSE; } - if (invert && evaluate_next_condition) { - DEBUG2("%.*s Converting !%s -> %s", - depth, filler, - (result != FALSE) ? "TRUE" : "FALSE", - (result == FALSE) ? "TRUE" : "FALSE"); - - - result = (result == FALSE); + if (invert) { + if (evaluate_next_condition) { + RDEBUG2("%.*s Converting !%s -> %s", + depth, filler, + (result != FALSE) ? "TRUE" : "FALSE", + (result == FALSE) ? "TRUE" : "FALSE"); + result = (result == FALSE); + } invert = FALSE; } @@ -271,76 +635,71 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, * condition */ p = end; - DEBUG4(">>> EVALUATE RETURNED ::%s::", end); - - if (!((*p == ')') || (*p == '!') || - ((p[0] == '&') && (p[1] == '&')) || - ((p[0] == '|') && (p[1] == '|')))) { + RDEBUG4(">>> AFTER RECURSION ... %s", end); - radlog(L_ERR, "Parse error in condition at: %s", p); + while ((*p == ' ') || (*p == '\t')) p++; + + if (!*p) { + radlog(L_ERR, "No closing brace"); return FALSE; } - if (*p == ')') p++; /* skip it */ + + if (*p == ')') p++; /* eat closing brace */ found_condition = TRUE; - + while ((*p == ' ') || (*p == '\t')) p++; + } + /* + * At EOL or closing brace, update && return. + */ + if (found_condition && (!*p || (*p == ')'))) break; + + /* + * Now it's either: + * + * WORD + * WORD1 op WORD2 + * && EXPR + * || EXPR + */ + if (found_condition) { /* - * EOL. It's OK. + * (A && B) means "evaluate B + * only if A was true" */ - if (!*p) { - DEBUG4(">>> AT EOL"); - *ptr = p; - *presult = result; - return TRUE; - - /* - * (A && B) means "evaluate B - * only if A was true" - */ - } else if ((p[0] == '&') && (p[1] == '&')) { - if (result == TRUE) { - evaluate_next_condition = evaluate_it; - } else { - evaluate_next_condition = FALSE; - } - p += 2; - - /* - * (A || B) means "evaluate B - * only if A was false" - */ - } else if ((p[0] == '|') && (p[1] == '|')) { - if (result == FALSE) { - evaluate_next_condition = evaluate_it; - } else { - evaluate_next_condition = FALSE; - } + if ((p[0] == '&') && (p[1] == '&')) { + if (!result) evaluate_next_condition = FALSE; p += 2; + found_condition = FALSE; + continue; /* go back to the start */ + } - } else if (*p == ')') { - DEBUG4(">>> CLOSING BRACE"); - *ptr = p; - *presult = result; - return TRUE; - - } else { - /* - * Parse error - */ - radlog(L_ERR, "Unexpected trailing text at: %s", p); - return FALSE; + /* + * (A || B) means "evaluate B + * only if A was false" + */ + if ((p[0] == '|') && (p[1] == '|')) { + if (result) evaluate_next_condition = FALSE; + p += 2; + found_condition = FALSE; + continue; } - } /* else it wasn't an opening brace */ - while ((*p == ' ') || (*p == '\t')) p++; + /* + * It must be: + * + * WORD + * WORD1 op WORD2 + */ + } - /* - * More conditions, keep going. - */ - if ((*p == '(') || (p[0] == '!')) continue; + if (found_condition) { + radlog(L_ERR, "Consecutive conditions at %s", p); + return FALSE; + } - DEBUG4(">>> LOOKING AT %s", p); + RDEBUG4(">>> LOOKING AT %s", p); start = p; /* @@ -352,7 +711,7 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, } /* - * Look for word == value + * Look for WORD1 op WORD2 */ lt = gettoken(&p, left, sizeof(left)); if ((lt != T_BARE_WORD) && @@ -375,63 +734,48 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, } /* - * Peek ahead. Maybe it's just a check for - * existence. If so, there shouldn't be anything - * else. + * Peek ahead, to see if it's: + * + * WORD + * + * or something else, such as + * + * WORD1 op WORD2 + * WORD ) + * WORD && EXPR + * WORD || EXPR */ q = p; while ((*q == ' ') || (*q == '\t')) q++; /* - * End of condition, + * If the next thing is: + * + * EOL + * end of condition + * && + * || + * + * Then WORD is just a test for existence. + * Remember that and skip ahead. */ if (!*q || (*q == ')') || - ((*q == '!') && (q[1] != '=') && (q[1] != '~')) || ((q[0] == '&') && (q[1] == '&')) || ((q[0] == '|') && (q[1] == '|'))) { - /* - * Check for truth or falsehood. - */ - if (all_digits(pleft)) { - lint = atoi(pleft); - result = (lint != 0); - - } else if (lt == T_BARE_WORD) { - result = (modreturn == lrad_str2int(modreturn_table, pleft, -1)); - } else { - result = (*pleft != '\0'); - } - - if (invert) { - DEBUG4(">>> INVERTING result"); - result = (result == FALSE); - invert = FALSE; - } - - if (evaluate_next_condition) { - DEBUG2("%.*s Evaluating %s\"%s\" -> %s", - depth, filler, - invert ? "!" : "", pleft, - (result != FALSE) ? "TRUE" : "FALSE"); - - } else if (request) { - DEBUG2("%.*s Skipping %s\"%s\"", - depth, filler, - invert ? "!" : "", pleft); - } - - DEBUG4(">>> I%d %d:%s", invert, - lt, left); - goto end_of_condition; + token = T_OP_CMP_TRUE; + rt = T_OP_INVALID; + pright = NULL; + goto do_cmp; } /* - * Else it's a full "foo == bar" thingy. + * Otherwise, it's: + * + * WORD1 op WORD2 */ 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; } @@ -451,6 +795,10 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { rt = getregex(&p, right, sizeof(right), &cflags); + if (rt != T_DOUBLE_QUOTED_STRING) { + radlog(L_ERR, "Expected regular expression at: %s", p); + return FALSE; + } } else #endif rt = gettoken(&p, right, sizeof(right)); @@ -474,198 +822,84 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, } } - DEBUG4(">>> %d:%s %d %d:%s", + RDEBUG4(">>> %d:%s %d %d:%s", lt, pleft, token, rt, pright); + do_cmp: if (evaluate_next_condition) { - switch (token) { - case T_OP_GT: - case T_OP_LE: - case T_OP_LT: - if (!all_digits(pleft)) { - radlog(L_ERR, "Left field is not a number at: %s", pleft); - return FALSE; - } - if (!all_digits(pright)) { - radlog(L_ERR, "Right field is not a number at: %s", pright); - return FALSE; - } - lint = atoi(pleft); - rint = atoi(pright); - break; - - default: - break; - } - - switch (token) { - case T_OP_CMP_EQ: - result = (strcmp(pleft, pright) == 0); - break; - - case T_OP_NE: - result = (strcmp(pleft, pright) != 0); - break; - - case T_OP_GE: - result = (lint >= rint); - break; - - case T_OP_GT: - result = (lint > rint); - break; - - case T_OP_LE: - result = (lint <= rint); - break; - - case T_OP_LT: - result = (lint < rint); - break; - -#ifdef HAVE_REGEX_H - case T_OP_REG_EQ: { - int i, compare; - regex_t reg; - regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; - - /* - * Include substring matches. - */ - regcomp(®, pright, cflags); - compare = regexec(®, pleft, - REQUEST_MAX_REGEX + 1, - rxmatch, 0); - regfree(®); - - /* - * Add %{0}, %{1}, etc. - */ - for (i = 0; i <= REQUEST_MAX_REGEX; i++) { - char *r; - char buffer[1024]; - - /* - * Didn't match: delete old - * match, if it existed. - */ - if ((compare != 0) || - (rxmatch[i].rm_so == -1)) { - r = request_data_get(request, - request, - REQUEST_DATA_REGEX | i); - if (r) { - free(r); - continue; - } - - /* - * No previous match - * to delete, stop. - */ - break; - } - - /* - * Copy substring into buffer. - */ - memcpy(buffer, pleft + 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... - */ - r = strdup(buffer); - request_data_add(request, request, - REQUEST_DATA_REGEX | i, - r, free); - } - result = (compare == 0); - } - break; - - case T_OP_REG_NE: { - int compare; - regex_t reg; - regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; - - /* - * Include substring matches. - */ - regcomp(®, pright, cflags); - compare = regexec(®, pleft, - REQUEST_MAX_REGEX + 1, - rxmatch, 0); - regfree(®); - - result = (compare != 0); + /* + * More parse errors. + */ + if (!radius_do_cmp(request, &result, lt, pleft, token, + rt, pright, cflags, modreturn)) { + return FALSE; } - break; -#endif + RDEBUG4(">>> Comparison returned %d", result); - default: - DEBUG4(">>> NOT IMPLEMENTED %d", token); - break; + if (invert) { + RDEBUG4(">>> INVERTING result"); + result = (result == FALSE); } - DEBUG2("%.*s Evaluating %s(%.*s) -> %s", + RDEBUG2("%.*s Evaluating %s(%.*s) -> %s", depth, filler, invert ? "!" : "", p - start, start, (result != FALSE) ? "TRUE" : "FALSE"); - DEBUG4(">>> GOT result %d", result); + invert = FALSE; + RDEBUG4(">>> GOT result %d", result); /* * Not evaluating it. We may be just * parsing it. */ } else if (request) { - DEBUG2("%.*s Skipping %s(\"%s\" %s \"%s\")", + RDEBUG2("%.*s Skipping %s(%.*s)", depth, filler, - invert ? "!" : "", pleft, comp, pright); + invert ? "!" : "", p - start, start); } - end_of_condition: - if (invert) { - DEBUG4(">>> INVERTING result"); - result = (result == FALSE); - invert = FALSE; - } + found_condition = TRUE; + } /* loop over the input condition */ - /* - * Don't evaluate it. - */ - DEBUG4(">>> EVALUATE %d ::%s::", - evaluate_next_condition, p); + if (!found_condition) { + radlog(L_ERR, "Syntax error. Expected condition at %s", p); + return FALSE; + } - while ((*p == ' ') || (*p == '\t')) p++; + RDEBUG4(">>> AT EOL -> %d", result); + *ptr = p; + if (evaluate_it) *presult = result; + return TRUE; +} +#endif - /* - * Closing brace or EOL, return. - */ - if (!*p || (*p == ')') || - ((*p == '!') && (p[1] != '=') && (p[1] != '~')) || - ((p[0] == '&') && (p[1] == '&')) || - ((p[0] == '|') && (p[1] == '|'))) { - DEBUG4(">>> AT EOL2a"); - *ptr = p; - if (evaluate_next_condition) *presult = result; - return TRUE; +static void fix_up(REQUEST *request) +{ + VALUE_PAIR *vp; + + request->username = NULL; + request->password = NULL; + + for (vp = request->packet->vps; vp != NULL; vp = vp->next) { + if (vp->vendor) continue; + + if ((vp->attribute == PW_USER_NAME) && + !request->username) { + request->username = vp; + + } else if (vp->attribute == PW_STRIPPED_USER_NAME) { + request->username = vp; + + } else if (vp->attribute == PW_USER_PASSWORD) { + request->password = vp; } - } /* loop over the input condition */ - DEBUG4(">>> AT EOL2b"); - *ptr = p; - if (evaluate_next_condition) *presult = result; - return TRUE; + if (request->username && request->password) continue; + } } + /* * The pairmove() function in src/lib/valuepair.c does all sorts of * extra magic that we don't want here. @@ -674,12 +908,12 @@ int radius_evaluate_condition(REQUEST *request, int modreturn, int depth, * only paircopy() those attributes that we're really going to * use. */ -static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) +void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) { int i, j, count, from_count, to_count, tailto; VALUE_PAIR *vp, *next, **last; VALUE_PAIR **from_list, **to_list; - int *edited; + int *edited = NULL; /* * Set up arrays for editing, to remove some of the @@ -730,7 +964,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) edited = rad_malloc(sizeof(*edited) * to_count); memset(edited, 0, sizeof(*edited) * to_count); - DEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count); + RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count); /* * Now that we have the lists initialized, start working @@ -739,7 +973,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) for (i = 0; i < from_count; i++) { int found; - DEBUG4("::: Examining %s", from_list[i]->name); + RDEBUG4("::: Examining %s", from_list[i]->name); /* * Attribute should be appended, OR the "to" list @@ -750,12 +984,13 @@ static void my_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; } @@ -772,7 +1007,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) * the one in the "from" list. */ if (from_list[i]->operator == T_OP_SET) { - DEBUG4("::: OVERWRITING %s FROM %d TO %d", + RDEBUG4("::: OVERWRITING %s FROM %d TO %d", to_list[j]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; @@ -792,10 +1027,19 @@ static void my_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" */ if ((from_list[i]->operator == T_OP_SUB) || + (from_list[i]->operator == T_OP_CMP_EQ) || (from_list[i]->operator == T_OP_LE) || (from_list[i]->operator == T_OP_GE)) { int rcode; @@ -821,9 +1065,14 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) from_list[i]->operator = old_op; switch (old_op) { + case T_OP_CMP_EQ: + if (rcode != 0) goto delete; + break; + case T_OP_SUB: if (rcode == 0) { - DEBUG4("::: DELETING %s FROM %d TO %d", + delete: + RDEBUG4("::: DELETING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = NULL; @@ -836,7 +1085,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) */ case T_OP_LE: if (rcode > 0) { - DEBUG4("::: REPLACING %s FROM %d TO %d", + RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; @@ -847,7 +1096,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) case T_OP_GE: if (rcode < 0) { - DEBUG4("::: REPLACING %s FROM %d TO %d", + RDEBUG4("::: REPLACING %s FROM %d TO %d", from_list[i]->name, i, j); pairfree(&to_list[j]); to_list[j] = from_list[i]; @@ -875,7 +1124,7 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) (from_list[i]->operator == T_OP_GE) || (from_list[i]->operator == T_OP_SET)) { append: - DEBUG4("::: APPENDING %s FROM %d TO %d", + RDEBUG4("::: APPENDING %s FROM %d TO %d", from_list[i]->name, i, tailto); to_list[tailto++] = from_list[i]; from_list[i] = NULL; @@ -893,37 +1142,50 @@ static void my_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from) } free(from_list); - DEBUG4("::: TO in %d out %d", to_count, tailto); + RDEBUG4("::: TO in %d out %d", to_count, tailto); /* * Re-chain the "to" list. */ *to = NULL; last = to; + for (i = 0; i < tailto; i++) { if (!to_list[i]) continue; - DEBUG4("::: to[%d] = %s", i, to_list[i]->name); + RDEBUG4("::: to[%d] = %s", i, to_list[i]->name); + + /* + * Mash the operator to a simple '='. The + * operators in the "to" list aren't used for + * anything. BUT they're used in the "detail" + * file and debug output, where we don't want to + * see the operators. + */ + to_list[i]->operator = T_OP_EQ; *last = to_list[i]; last = &(*last)->next; + } - /* - * Fix dumb cache issues - */ - if ((i >= to_count) || edited[i]) { - if (to_list[i]->attribute == PW_USER_NAME) { - request->username = to_list[i]; - - } else if (to_list[i]->attribute == PW_USER_PASSWORD) { - request->password = to_list[i]; - } - } + rad_assert(request != NULL); + rad_assert(request->packet != NULL); + + /* + * Fix dumb cache issues + */ + if (to == &request->packet->vps) { + fix_up(request); + } else if (request->parent && (to == &request->parent->packet->vps)) { + fix_up(request->parent); } + free(to_list); + free(edited); } +#ifdef WITH_UNLANG /* * Copied shamelessly from conffile.c, to simplify the API for * now... @@ -946,8 +1208,8 @@ struct conf_pair { CONF_ITEM item; char *attr; char *value; - LRAD_TOKEN operator; - LRAD_TOKEN value_type; + FR_TOKEN operator; + FR_TOKEN value_type; }; /* @@ -959,26 +1221,78 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs, CONF_ITEM *ci; VALUE_PAIR *newlist, *vp; VALUE_PAIR **output_vps = NULL; + REQUEST *myrequest = request; if (!request || !cs) return RLM_MODULE_INVALID; + /* + * If we are an inner tunnel session, permit the + * policy to update the outer lists directly. + */ + if (strncmp(name, "outer.", 6) == 0) { + if (!request->parent) return RLM_MODULE_NOOP; + + myrequest = request->parent; + name += 6; + } + if (strcmp(name, "request") == 0) { - output_vps = &request->packet->vps; + output_vps = &myrequest->packet->vps; } else if (strcmp(name, "reply") == 0) { - output_vps = &request->reply->vps; + output_vps = &myrequest->reply->vps; +#ifdef WITH_PROXY } else if (strcmp(name, "proxy-request") == 0) { - if (request->proxy) output_vps = &request->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->config_items; + output_vps = &myrequest->config_items; } else if (strcmp(name, "control") == 0) { - output_vps = &request->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; @@ -988,7 +1302,7 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs, newlist = paircopy(input_vps); if (!newlist) { - DEBUG2("Out of memory"); + RDEBUG2("Out of memory"); return RLM_MODULE_FAIL; } @@ -1008,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. @@ -1024,8 +1345,8 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs, } if (!pairparsevalue(vp, value)) { - DEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s", - value, vp->name, librad_errstr); + RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s", + value, vp->name, fr_strerror()); pairfree(&newlist); return RLM_MODULE_FAIL; } @@ -1034,7 +1355,8 @@ int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs, vp = vp->next; } - my_pairmove(request, output_vps, newlist); + radius_pairmove(request, output_vps, newlist); return RLM_MODULE_UPDATED; } +#endif