📄 evaluate.c
字号:
/* * evaluate.c Evaluate complex conditions * * Version: $Id: evaluate.c,v 1.42 2008/05/02 09:11:18 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2007 The FreeRADIUS server project * Copyright 2007 Alan DeKok <aland@deployingradius.com> */#include <freeradius-devel/ident.h>RCSID("$Id: evaluate.c,v 1.42 2008/05/02 09:11:18 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/rad_assert.h>#include <ctype.h>#ifdef HAVE_REGEX_H#include <regex.h>/* * For POSIX Regular expressions. * (0) Means no extended regular expressions. * REG_EXTENDED means use extended regular expressions. */#ifndef REG_EXTENDED#define REG_EXTENDED (0)#endif#ifndef REG_NOSUB#define REG_NOSUB (0)#endif#endifstatic int all_digits(const char *string){ const char *p = string; if (*p == '-') p++; while (isdigit((int) *p)) p++; return (*p == '\0');}#ifndef NDEBUG#ifndef DEBUG4#define DEBUG4 if (debug_flag > 4)log_debug#endif#else#define DEBUG4 if (0) log_debug#endifstatic const char *filler = "????????????????????????????????????????????????????????????????";static const char *expand_string(char *buffer, size_t sizeof_buffer, REQUEST *request, FR_TOKEN value_type, const char *value){ int result; char *p; switch (value_type) { default: case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: return value; case T_BACK_QUOTED_STRING: result = radius_exec_program(value, request, 1, buffer, sizeof_buffer, NULL, NULL, 0); if (result != 0) { return NULL; } /* * The result should be ASCII. */ for (p = buffer; *p != '\0'; p++) { if (*p < ' ' ) { *p = '\0'; return buffer; } } return buffer; case T_DOUBLE_QUOTED_STRING: if (!strchr(value, '%')) return value; radius_xlat(buffer, sizeof_buffer, value, request, NULL); return buffer; } return NULL;}#ifdef HAVE_REGEX_Hstatic FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen, int *pcflags){ const char *p = *ptr; char *q = buffer; if (*p != '/') return T_OP_INVALID; *pcflags = REG_EXTENDED; p++; while (*p) { if (buflen <= 1) break; if (*p == '/') { p++; /* * Check for case insensitivity */ if (*p == 'i') { p++; *pcflags |= REG_ICASE; } break; } if (*p == '\\') { int x; switch (p[1]) { case 'r': *q++ = '\r'; break; case 'n': *q++ = '\n'; break; case 't': *q++ = '\t'; break; case '"': *q++ = '"'; break; case '\'': *q++ = '\''; break; case '`': *q++ = '`'; break; /* * FIXME: add 'x' and 'u' */ default: if ((p[1] >= '0') && (p[1] <= '9') && (sscanf(p, "%3o", &x) == 1)) { *q++ = x; p += 2; } else { *q++ = p[1]; } break; } p += 2; buflen--; continue; } *(q++) = *(p++); buflen--; } *q = '\0'; *ptr = p; return T_DOUBLE_QUOTED_STRING;}#endifstatic const FR_NAME_NUMBER modreturn_table[] = { { "reject", RLM_MODULE_REJECT }, { "fail", RLM_MODULE_FAIL }, { "ok", RLM_MODULE_OK }, { "handled", RLM_MODULE_HANDLED }, { "invalid", RLM_MODULE_INVALID }, { "userlock", RLM_MODULE_USERLOCK }, { "notfound", RLM_MODULE_NOTFOUND }, { "noop", RLM_MODULE_NOOP }, { "updated", RLM_MODULE_UPDATED }, { NULL, 0 }};static 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; } 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; } 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; } 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); 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; int lint, rint; VALUE_PAIR *vp = NULL; char buffer[1024]; 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) { DEBUG2(" (Attribute %s was not found)", pleft); return FALSE; }#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)) { DEBUG2("Failed parsing \"%s\": %s", pright, librad_errstr); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); return TRUE; } /* else it's not a attribute in the dictionary */ } do_checks: switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { DEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = atoi(pright); if (!all_digits(pleft)) { DEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = atoi(pleft); 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 = atoi(pleft); 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. */ regcomp(®, pright, cflags); 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 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); } break;#endif default: DEBUG4(">>> 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){ int found_condition = FALSE; int result = TRUE; int invert = FALSE; int evaluate_next_condition = evaluate_it; const char *p = *ptr; const char *q, *start; FR_TOKEN token, lt, rt; char left[1024], right[1024], comp[4]; const char *pleft, *pright; char xleft[1024], xright[1024];#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; } while (*p) { while ((*p == ' ') || (*p == '\t')) p++; if (*p == '!') { DEBUG4(">>> INVERT"); invert = TRUE; p++; } /* * It's a subcondition. */ if (*p == '(') { const char *end = p + 1; /* * Evaluate the condition, bailing out on * parse error. */ DEBUG4(">>> CALLING EVALUATE %s", end); if (!radius_evaluate_condition(request, modreturn, depth + 1, &end, evaluate_next_condition, &result)) { 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); invert = FALSE; } /* * Start from the end of the previous * condition */ p = end; DEBUG4(">>> EVALUATE RETURNED ::%s::", end); if (!((*p == ')') || (*p == '!') || ((p[0] == '&') && (p[1] == '&')) || ((p[0] == '|') && (p[1] == '|')))) { radlog(L_ERR, "Parse error in condition at: %s", p); return FALSE; } if (*p == ')') p++; /* skip it */ found_condition = TRUE; while ((*p == ' ') || (*p == '\t')) p++; /* * EOL. It's OK. */ 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; } p += 2; } else if (*p == ')') { DEBUG4(">>> CLOSING BRACE"); *ptr = p; *presult = result; return TRUE; } else { /* * Parse error */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -