2 * evaluate.c Evaluate a policy language
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2004 Alan DeKok <aland@ox.org>
23 #include "rlm_policy.h"
29 #define debug_evaluate if (0) printf
32 * Print stuff we've parsed
34 static void policy_print(const policy_item_t *item, int indent)
37 if (indent) printf("%*s", indent, " ");
45 if (indent) printf("%*s", indent, " ");
46 printf("[BAD STATEMENT]");
49 case POLICY_TYPE_PRINT:
50 if (indent) printf("%*s", indent, " ");
52 const policy_print_t *this;
54 this = (const policy_print_t *) item;
56 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
57 printf("print %s\n", this->rhs);
59 printf("print \"%s\"\n", this->rhs);
64 case POLICY_TYPE_ASSIGNMENT:
66 const policy_assignment_t *assign;
68 assign = (const policy_assignment_t *) item;
69 if (indent) printf("%*s", indent, " ");
71 printf("\t%s %s ", assign->lhs,
72 lrad_int2str(rlm_policy_tokens,
73 assign->assign, "?"));
74 if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
75 printf("%s\n", assign->rhs);
80 printf("\"%s\"\n", assign->rhs);
85 case POLICY_TYPE_CONDITIONAL: /* no indentation here */
87 const policy_condition_t *condition;
89 condition = (const policy_condition_t *) item;
96 if (condition->compare == POLICY_LEX_L_BRACKET) {
97 policy_print(condition->child, indent);
102 if (condition->compare == POLICY_LEX_L_NOT) {
104 policy_print(condition->child, indent);
109 if (condition->compare == POLICY_LEX_CMP_TRUE) {
110 printf("%s)", condition->lhs);
114 if (condition->lhs_type == POLICY_LEX_FUNCTION) {
115 printf("%s()", condition->lhs);
119 * and move all of this logic
122 printf("\"%s\"", condition->lhs);
126 * We always print this condition.
128 printf(" %s ", lrad_int2str(rlm_policy_tokens,
131 if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
132 printf("%s", condition->rhs);
136 * and move all of this logic
139 printf("\"%s\"", condition->rhs);
143 if ((condition->child_condition != POLICY_LEX_BAD) &&
144 (condition->child_condition != POLICY_LEX_BARE_WORD)) {
145 printf(" %s ", lrad_int2str(rlm_policy_tokens, condition->child_condition, "?"));
146 policy_print(condition->child, indent);
153 const policy_if_t *statement;
155 statement = (const policy_if_t *) item;
157 if (indent) printf("%*s", indent, " ");
159 policy_print(statement->condition, indent);
161 policy_print(statement->if_true, indent + 1);
162 if (indent) printf("%*s", indent, " ");
163 if (statement->if_false) {
165 if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
167 policy_print(statement->if_false, indent + 1);
168 if (indent) printf("%*s", indent, " ");
171 policy_print(statement->if_false, indent + 1);
179 case POLICY_TYPE_ATTRIBUTE_LIST:
181 const policy_attributes_t *this;
183 this = (const policy_attributes_t *) item;
185 if (indent) printf("%*s", indent, " ");
187 lrad_int2str(policy_reserved_words,
189 lrad_int2str(rlm_policy_tokens,
191 policy_print(this->attributes, indent + 1);
192 if (indent) printf("%*s", indent, " ");
197 case POLICY_TYPE_NAMED_POLICY:
199 const policy_named_t *this;
201 this = (const policy_named_t *) item;
202 if (indent) printf("%*s", indent, " ");
203 printf("policy %s {\n", this->name);
204 policy_print(this->policy, indent + 1);
205 if (indent) printf("%*s", indent, " ");
210 case POLICY_TYPE_CALL:
212 const policy_call_t *this;
214 this = (const policy_call_t *) item;
215 if (indent) printf("%*s", indent, " ");
216 printf("call %s\n", this->name);
220 case POLICY_TYPE_RETURN:
222 const policy_return_t *this;
224 this = (const policy_return_t *) item;
225 if (indent) printf("%*s", indent, " ");
226 printf("return %s\n",
227 lrad_int2str(policy_return_codes,
228 this->rcode, "???"));
232 case POLICY_TYPE_MODULE:
234 const policy_module_t *this;
236 this = (const policy_module_t *) item;
237 if (indent) printf("%*s", indent, " ");
238 printf("module %s <stuff>\n",
239 lrad_int2str(policy_component_names,
240 this->component, "???"));
245 if (indent) printf("%*s", indent, " ");
256 void rlm_policy_print(const policy_item_t *item)
258 printf("----------------------------------------------------------\n");
259 policy_print(item, 0);
260 printf("----------------------------------------------------------\n");
264 * Internal stack of things to do. This lets us have function
267 * Yes, we should learn lex, yacc, etc.
269 #define POLICY_MAX_STACK 16
270 typedef struct policy_state_t {
272 REQUEST *request; /* so it's not passed on the C stack */
273 int rcode; /* for functions, etc. */
274 int component; /* for calling other modules */
276 const policy_item_t *stack[POLICY_MAX_STACK];
280 static int policy_evaluate_name(policy_state_t *state, const char *name);
283 * Push an item onto the state.
285 static int policy_stack_push(policy_state_t *state, const policy_item_t *item)
287 rad_assert(state->depth >= 0);
290 * Asked to push nothing. Don't push it.
295 * State is full. Die.
297 if (state->depth >= POLICY_MAX_STACK) {
302 * Walk back up the stack, looking for previous ocurrances
303 * of this name. If found, we have infinite recursion,
304 * which we stop dead in the water!
306 * This isn't strictly necessary right now, as we look up
307 * policies by name when they're first referenced. This
308 * means that ALL references are backwards (to the start
309 * of the file), which means that there are no circular
312 if (item->type == POLICY_TYPE_NAMED_POLICY) {
315 for (i = 0; i < state->depth; i++) {
317 * Check for circular references, by seeing
318 * if the function is already on the stack.
320 * Hmmm... do we want to do this for any type?
322 if (state->stack[i] == item) {
323 debug_evaluate("Circular call to policy %s\n",
324 ((const policy_named_t *) item)->name);
330 debug_evaluate("push %d %p\n", state->depth, item);
332 state->stack[state->depth] = item;
333 state->depth++; /* points to unused entry */
340 * Pop an item from the state.
342 static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)
344 rad_assert(pitem != NULL);
345 rad_assert(state->depth >= 0);
348 if (state->depth == 0) {
353 *pitem = state->stack[state->depth - 1];
356 * Named policies are on the stack for catching recursion.
358 if ((*pitem)->type == POLICY_TYPE_NAMED_POLICY) {
364 * Process the whole item list.
366 if ((*pitem)->next) {
367 state->stack[state->depth - 1] = (*pitem)->next;
368 debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem);
370 state->depth--; /* points to unused entry */
371 debug_evaluate("pop %d %p\n", state->depth, *pitem);
379 * Evaluate a print statement
381 static int evaluate_print(policy_state_t *state, const policy_item_t *item)
383 const policy_print_t *this;
385 this = (const policy_print_t *) item;
387 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
388 printf("%s\n", this->rhs);
392 radius_xlat(buffer, sizeof(buffer), this->rhs,
393 state->request, NULL);
394 printf("%s", buffer);
398 * Doesn't change state->rcode
405 * Return a VALUE_PAIR, given an attribute name.
407 * FIXME: Have it return the N'th one, too, like
410 * The amount of duplicated code is getting annoying...
412 static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
415 const DICT_ATTR *dattr;
419 vps = request->packet->vps;;
422 * FIXME: use names from reserved word list?
424 if (strncasecmp(name, "request:", 8) == 0) {
426 } else if (strncasecmp(name, "reply:", 6) == 0) {
428 vps = request->reply->vps;
429 } else if (strncasecmp(name, "proxy-request:", 14) == 0) {
431 if (request->proxy) {
432 vps = request->proxy->vps;
434 } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {
436 if (request->proxy_reply) {
437 vps = request->proxy_reply->vps;
439 } else if (strncasecmp(name, "control:", 8) == 0) {
441 vps = request->config_items;
442 } /* else it must be a bare attribute name */
448 dattr = dict_attrbyname(p);
450 fprintf(stderr, "No such attribute %s\n", p);
451 return NULL; /* no such attribute */
454 return pairfind(vps, dattr->attr);
459 * Evaluate an assignment
461 * Not really used much...
463 static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)
465 const policy_assignment_t *this;
467 const DICT_ATTR *dattr;
470 this = (const policy_assignment_t *) item;
472 rad_assert(this->lhs != NULL);
473 rad_assert(this->rhs != NULL);
476 dattr = dict_attrbyname(this->lhs);
478 fprintf(stderr, "HUH?\n");
488 * Evaluate a condition
490 static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
493 const policy_condition_t *this;
494 VALUE_PAIR *vp = NULL;
495 const char *data = NULL;
501 char lhs_buffer[2048];
503 this = (const policy_condition_t *) item;
507 * FIXME: Don't always do this...
509 if (this->compare != POLICY_LEX_L_BRACKET) {
510 if (this->lhs_type == POLICY_LEX_FUNCTION) {
512 * We can't call evaluate_call here,
513 * because that just pushes stuff onto
514 * the stack, and we want to actually
515 * evaluate all of it...
517 rcode = policy_evaluate_name(state, this->lhs);
518 data = lrad_int2str(policy_return_codes, rcode, "???");
519 strNcpy(lhs_buffer, data, sizeof(lhs_buffer)); /* FIXME: yuck */
520 } else if (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) {
521 if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs,
522 state->request, NULL) > 0) {
528 switch (this->compare) {
529 case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */
530 rcode = evaluate_condition(state, this->child);
533 case POLICY_LEX_L_NOT:
534 rcode = evaluate_condition(state, this->child);
535 rcode = (rcode == FALSE); /* reverse sense of test */
538 case POLICY_LEX_CMP_FALSE: /* non-existence */
539 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
540 vp = find_vp(state->request, this->lhs);
541 rcode = (vp == NULL);
543 rcode = (data == NULL);
547 case POLICY_LEX_CMP_TRUE: /* existence */
548 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
549 vp = find_vp(state->request, this->lhs);
550 rcode = (vp != NULL);
552 rcode = (data != NULL);
556 default: /* process other comparisons */
557 if ((this->compare != POLICY_LEX_CMP_EQUALS) &&
559 (this->compare != POLICY_LEX_RX_EQUALS) &&
560 (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&
562 (this->compare != POLICY_LEX_LT) &&
563 (this->compare != POLICY_LEX_GT) &&
564 (this->compare != POLICY_LEX_LE) &&
565 (this->compare != POLICY_LEX_GE) &&
566 (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) {
567 fprintf(stderr, "%d: bad comparison\n",
572 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
575 vp = find_vp(state->request, this->lhs);
578 * A op B always returns FALSE if A doesn't
581 if (!vp) return FALSE; /* not in the request */
584 * FIXME: Move sanity checks to
585 * post-parse code, so we don't do
586 * it on every packet.
588 vp_prints_value(buffer, sizeof(buffer), vp, 0);
589 myvp = pairmake(vp->name, this->rhs, T_OP_EQ);
593 * FIXME: What to do about comparisons
594 * where vp doesn't exist? Right now,
595 * "simplepaircmp" returns -1, which is
596 * probably a bad idea. it should
597 * instead take an operator, a pointer to
598 * the comparison result, and return
599 * "true/false" for "comparions
600 * succeeded/failed", which are different
601 * error codes than "comparison is less
602 * than, equal to, or greater than zero".
604 compare = simplepaircmp(state->request,
610 * FIXME: Do something for RHS type?
612 printf("CMP %s %s\n", lhs_buffer, this->rhs);
613 compare = strcmp(lhs_buffer, this->rhs);
616 debug_evaluate("CONDITION COMPARE %d\n", compare);
618 switch (this->compare) {
619 case POLICY_LEX_CMP_EQUALS:
620 rcode = (compare == 0);
623 case POLICY_LEX_CMP_NOT_EQUALS:
624 rcode = (compare != 0);
628 rcode = (compare < 0);
632 rcode = (compare > 0);
636 rcode =(compare <= 0);
640 rcode = (compare >= 0);
644 case POLICY_LEX_RX_EQUALS:
645 { /* FIXME: copied from src/main/valuepair.c */
647 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
650 * Include substring matches.
652 if (regcomp(®, this->rhs,
653 REG_EXTENDED) != 0) {
656 rad_assert(data != NULL);
657 rcode = regexec(®, data,
658 REQUEST_MAX_REGEX + 1,
660 rcode = (rcode == 0);
664 * Add %{0}, %{1}, etc.
666 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
671 * Didn't match: delete old
672 * match, if it existed.
675 (rxmatch[i].rm_so == -1)) {
676 p = request_data_get(state->request, state->request,
677 REQUEST_DATA_REGEX | i);
691 * Copy substring into buffer.
694 data + rxmatch[i].rm_so,
695 rxmatch[i].rm_eo - rxmatch[i].rm_so);
696 rxbuffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
699 * Copy substring, and add it to
702 * Note that we don't check
703 * for out of memory, which is
704 * the only error we can get...
706 p = strdup(rxbuffer);
707 request_data_add(state->request,
709 REQUEST_DATA_REGEX | i,
716 case POLICY_LEX_RX_NOT_EQUALS:
717 regcomp(®, this->rhs, REG_EXTENDED|REG_NOSUB);
718 rad_assert(data != NULL);
719 rcode = regexec(®, data,
721 rcode = (rcode != 0);
724 #endif /* HAVE_REGEX_H */
728 } /* switch over comparison operators */
729 break; /* default from first switch over compare */
735 switch (this->child_condition) {
739 case POLICY_LEX_L_AND:
740 if (!rcode) return rcode; /* FALSE && x == FALSE */
743 case POLICY_LEX_L_OR:
744 if (rcode) return rcode; /* TRUE && x == TRUE */
751 this = (const policy_condition_t *) this->child;
754 return 1; /* should never reach here */
759 * Evaluate an 'if' statement
761 static int evaluate_if(policy_state_t *state, const policy_item_t *item)
764 const policy_if_t *this;
766 this = (const policy_if_t *) item;
769 * evaluate_condition calls itself recursively.
770 * We should probably allocate a new state, instead.
772 rcode = evaluate_condition(state, this->condition);
773 debug_evaluate("IF condition returned %s\n",
774 rcode ? "true" : "false");
776 rcode = policy_stack_push(state, this->if_true);
777 if (!rcode) return rcode;
778 } else if (this->if_false) {
779 rcode = policy_stack_push(state, this->if_false);
780 if (!rcode) return rcode;
784 * 'if' can fail, if the block it's processing fails.
791 * Make a VALUE_PAIR from a policy_assignment_t*
793 * The assignment operator has to be '='.
795 static VALUE_PAIR *assign2vp(REQUEST *request,
796 const policy_assignment_t *assign)
799 LRAD_TOKEN operator = T_OP_EQ;
800 const char *value = assign->rhs;
803 if ((assign->rhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) &&
804 (strchr(assign->rhs, '%') != NULL)) {
805 radius_xlat(buffer, sizeof(buffer), assign->rhs,
811 * This is crappy.. fix it.
813 switch (assign->assign) {
814 case POLICY_LEX_ASSIGN:
818 case POLICY_LEX_SET_EQUALS:
822 case POLICY_LEX_PLUS_EQUALS:
827 fprintf(stderr, "Expected '=' for operator, not '%s' at line %d\n",
828 lrad_int2str(rlm_policy_tokens,
829 assign->assign, "?"),
830 assign->item.lineno);
834 vp = pairmake(assign->lhs, value, operator);
836 fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);
844 * Evaluate a 'packet .= {attrs}' statement
846 static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
848 const policy_attributes_t *this;
849 VALUE_PAIR **vps = NULL;
850 VALUE_PAIR *vp, *head, **tail;
851 const policy_item_t *attr;
853 this = (const policy_attributes_t *) item;
855 switch (this->where) {
856 case POLICY_RESERVED_CONTROL:
857 vps = &(state->request->config_items);
860 case POLICY_RESERVED_REQUEST:
861 vps = &(state->request->packet->vps);
864 case POLICY_RESERVED_REPLY:
865 vps = &(state->request->reply->vps);
868 case POLICY_RESERVED_PROXY_REQUEST:
869 if (!state->request->proxy) return 0; /* FIXME: print error */
870 vps = &(state->request->proxy->vps);
873 case POLICY_RESERVED_PROXY_REPLY:
874 if (!state->request->proxy_reply) return 0; /* FIXME: print error */
875 vps = &(state->request->proxy_reply->vps);
885 for (attr = this->attributes; attr != NULL; attr = attr->next) {
886 if (attr->type != POLICY_TYPE_ASSIGNMENT) {
887 fprintf(stderr, "bad assignment in attribute list at line %d\n", attr->lineno);
892 vp = assign2vp(state->request, (const policy_assignment_t *) attr);
894 fprintf(stderr, "Failed to allocate VP\n");
903 case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
908 case POLICY_LEX_ASSIGN: /* 'union' */
909 pairmove(vps, &head);
913 case POLICY_LEX_CONCAT_EQUALS:
918 fprintf(stderr, "HUH?\n");
923 state->rcode = RLM_MODULE_UPDATED; /* we did stuff */
930 * Evaluate a reference call to a module.
932 static int evaluate_call(policy_state_t *state, const policy_item_t *item)
935 const policy_call_t *this;
936 const policy_named_t *policy;
938 this = (const policy_call_t *) item;
940 policy = rlm_policy_find(state->inst->policies, this->name);
941 if (!policy) return 0; /* not found... */
943 DEBUG2("rlm_policy: Evaluating policy %s", this->name);
945 rad_assert(policy->policy->type != POLICY_TYPE_BAD);
946 rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);
949 * Push the name of the function onto the stack,
950 * so that we can catch recursive calls.
952 * The "pop" function will skip over it when it sees it.
954 rcode = policy_stack_push(state, (const policy_item_t *) policy);
960 * Push it onto the stack. Other code will take care of
963 rcode = policy_stack_push(state, policy->policy);
973 * Evaluate a return statement
975 static int evaluate_return(policy_state_t *state, const policy_item_t *item)
977 const policy_return_t *this;
979 this = (const policy_return_t *) item;
980 state->rcode = this->rcode;
982 return 1; /* we succeeded */
987 * Evaluate a module statement
989 static int evaluate_module(policy_state_t *state, const policy_item_t *item)
991 const policy_module_t *this;
993 this = (const policy_module_t *) item;
996 * Just to be paranoid. Maybe we want to loosen this
997 * restriction in the future?
999 if (this->component != state->component) {
1000 DEBUG2("rlm_policy: Cannot mix & match components");
1004 DEBUG2("rlm_policy: begin nested call");
1005 state->rcode = modcall(this->component, this->mc, state->request);
1006 DEBUG2("rlm_policy: end nested call");
1008 return 1; /* we succeeded */
1013 * State machine stuff.
1015 typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);
1019 * MUST be kept in sync with policy_type_t
1021 static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {
1022 NULL, /* POLICY_TYPE_BAD */
1025 evaluate_assignment,
1028 NULL, /* define a named policy.. */
1036 * Evaluate a policy, keyed by name.
1038 static int policy_evaluate_name(policy_state_t *state, const char *name)
1041 const policy_item_t *this;
1042 policy_named_t mypolicy, *policy;
1044 mypolicy.name = name;
1045 policy = rbtree_finddata(state->inst->policies, &mypolicy);
1046 if (!policy) return RLM_MODULE_FAIL;
1048 DEBUG2("rlm_policy: Evaluating policy %s", name);
1050 rad_assert(policy->item.type != POLICY_TYPE_BAD);
1051 rad_assert(policy->item.type < POLICY_TYPE_NUM_TYPES);
1053 rcode = policy_stack_push(state, policy->policy);
1055 return RLM_MODULE_FAIL;
1059 * FIXME: Look for magic keywords like "return",
1060 * where the packet gets accepted/rejected/whatever
1062 while (policy_stack_pop(state, &this)) {
1063 rad_assert(this != NULL);
1064 rad_assert(this->type != POLICY_TYPE_BAD);
1065 rad_assert(this->type < POLICY_TYPE_NUM_TYPES);
1067 debug_evaluate("Evaluating at line %d\n",
1069 rcode = (*evaluate_functions[this->type])(state,
1072 return RLM_MODULE_FAIL;
1074 } /* loop until the stack is empty */
1076 return state->rcode;
1081 * Evaluate, which is pretty close to print, but we look at what
1084 int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)
1087 policy_state_t *state;
1089 state = rad_malloc(sizeof(*state));
1090 memset(state, 0, sizeof(*state));
1091 state->request = request;
1093 state->rcode = RLM_MODULE_OK;
1094 state->component = lrad_str2int(policy_component_names, name,
1095 RLM_COMPONENT_COUNT);
1097 rcode = policy_evaluate_name(state, name);
1101 return rcode; /* evaluated OK. */