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"
31 #define debug_evaluate if (0) printf
34 * Print stuff we've parsed
36 static void policy_print(const policy_item_t *item, int indent)
39 if (indent) printf("%*s", indent, " ");
47 if (indent) printf("%*s", indent, " ");
48 printf("[BAD STATEMENT]");
51 case POLICY_TYPE_PRINT:
52 if (indent) printf("%*s", indent, " ");
54 const policy_print_t *this;
56 this = (const policy_print_t *) item;
58 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
59 printf("print %s\n", this->rhs);
61 printf("print \"%s\"\n", this->rhs);
66 case POLICY_TYPE_ASSIGNMENT:
68 const policy_assignment_t *assign;
70 assign = (const policy_assignment_t *) item;
71 if (indent) printf("%*s", indent, " ");
73 printf("\t%s %s ", assign->lhs,
74 lrad_int2str(rlm_policy_tokens,
75 assign->assign, "?"));
76 if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
77 printf("%s\n", assign->rhs);
82 printf("\"%s\"\n", assign->rhs);
87 case POLICY_TYPE_CONDITIONAL: /* no indentation here */
89 const policy_condition_t *condition;
91 condition = (const policy_condition_t *) item;
98 if (condition->compare == POLICY_LEX_L_BRACKET) {
99 policy_print(condition->child, indent);
104 if (condition->compare == POLICY_LEX_L_NOT) {
106 policy_print(condition->child, indent);
111 if (condition->compare == POLICY_LEX_CMP_TRUE) {
112 printf("%s)", condition->lhs);
116 if (condition->lhs_type == POLICY_LEX_BARE_WORD) {
117 printf("%s", condition->lhs);
121 * and move all of this logic
124 printf("\"%s\"", condition->lhs);
128 * We always print this condition.
130 printf(" %s ", lrad_int2str(rlm_policy_tokens,
133 if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
134 printf("%s", condition->rhs);
138 * and move all of this logic
141 printf("\"%s\"", condition->rhs);
145 if (condition->child_condition != POLICY_LEX_BAD) {
146 printf(" %s ", lrad_int2str(rlm_policy_tokens, condition->child_condition, "?"));
147 policy_print(condition->child, indent);
154 const policy_if_t *statement;
156 statement = (const policy_if_t *) item;
158 if (indent) printf("%*s", indent, " ");
160 policy_print(statement->condition, indent);
162 policy_print(statement->if_true, indent + 1);
163 if (indent) printf("%*s", indent, " ");
164 if (statement->if_false) {
166 if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
168 policy_print(statement->if_false, indent + 1);
169 if (indent) printf("%*s", indent, " ");
172 policy_print(statement->if_false, indent + 1);
180 case POLICY_TYPE_ATTRIBUTE_LIST:
182 const policy_attributes_t *this;
184 this = (const policy_attributes_t *) item;
186 if (indent) printf("%*s", indent, " ");
188 lrad_int2str(policy_reserved_words,
190 lrad_int2str(rlm_policy_tokens,
192 policy_print(this->attributes, indent + 1);
193 if (indent) printf("%*s", indent, " ");
198 case POLICY_TYPE_NAMED_POLICY:
200 const policy_named_t *this;
202 this = (const policy_named_t *) item;
203 if (indent) printf("%*s", indent, " ");
204 printf("policy %s {\n", this->name);
205 policy_print(this->policy, indent + 1);
206 if (indent) printf("%*s", indent, " ");
211 case POLICY_TYPE_CALL:
213 const policy_call_t *this;
215 this = (const policy_call_t *) item;
216 if (indent) printf("%*s", indent, " ");
217 printf("call %s\n", this->name);
222 if (indent) printf("%*s", indent, " ");
233 void rlm_policy_print(const policy_item_t *item)
235 printf("----------------------------------------------------------\n");
236 policy_print(item, 0);
237 printf("----------------------------------------------------------\n");
241 * Internal stack of things to do.
243 * When a function is about to be pushed onto the stack, we walk
244 * backwards through the stack, and ensure that the function is
245 * not already there. This prevents infinite recursion.
247 * This means that we NEVER pop functions. Rather, we push the
248 * function, and then immediately push it's first element.
250 * When we've finished popping all of the elements, we pop the
251 * function, realize it's a function, ignore it, and pop one more
254 #define POLICY_MAX_STACK 16
255 typedef struct policy_state_t {
258 REQUEST *request; /* so it's not passed on the C stack */
259 const policy_item_t *stack[POLICY_MAX_STACK];
264 * Push an item onto the state.
266 static int policy_stack_push(policy_state_t *state, const policy_item_t *item)
268 rad_assert(state->depth >= 0);
271 * Asked to push nothing. Don't push it.
276 * State is full. Die.
278 if (state->depth >= POLICY_MAX_STACK) {
283 * Walk back up the stack, looking for previous ocurrances
284 * of this name. If found, we have infinite recursion,
285 * which we stop dead in the water!
287 * This isn't strictly necessary right now, as we look up
288 * policies by name when they're first referenced. This
289 * means that ALL references are backwards (to the start
290 * of the file), which means that there are no circular
293 if (item->type == POLICY_TYPE_NAMED_POLICY) {
296 for (i = 0; i < state->depth; i++) {
298 * Check for circular references, by seeing
299 * if the function is already on the stack.
301 * Hmmm... do we want to do this for any type?
303 if (state->stack[i] == item) {
304 debug_evaluate("Circular call to policy %s\n",
305 ((const policy_named_t *) item)->name);
311 debug_evaluate("push %d %p\n", state->depth, item);
313 state->stack[state->depth] = item;
314 state->depth++; /* points to unused entry */
321 * Pop an item from the state.
323 static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)
325 rad_assert(pitem != NULL);
326 rad_assert(state->depth >= 0);
328 if (state->depth == 0) {
333 *pitem = state->stack[state->depth - 1];
336 * Process the whole item list.
338 if ((*pitem)->next) {
339 state->stack[state->depth - 1] = (*pitem)->next;
340 debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem);
342 state->depth--; /* points to unused entry */
343 debug_evaluate("pop %d %p\n", state->depth, *pitem);
351 * Evaluate a print statement
353 static int evaluate_print(policy_state_t *state, const policy_item_t *item)
355 const policy_print_t *this;
357 this = (const policy_print_t *) item;
359 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
360 printf("%s\n", this->rhs);
364 radius_xlat(buffer, sizeof(buffer), this->rhs,
365 state->request, NULL);
366 printf("%s", buffer);
373 * Return a VALUE_PAIR, given an attribute name.
375 * FIXME: Have it return the N'th one, too, like
378 * The amount of duplicated code is getting annoying...
380 static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
383 const DICT_ATTR *dattr;
387 vps = request->packet->vps;;
390 * FIXME: use names from reserved word list?
392 if (strncasecmp(name, "request:", 8) == 0) {
394 } else if (strncasecmp(name, "reply:", 6) == 0) {
396 vps = request->reply->vps;
397 } else if (strncasecmp(name, "proxy-request:", 14) == 0) {
399 if (request->proxy) {
400 vps = request->proxy->vps;
402 } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {
404 if (request->proxy_reply) {
405 vps = request->proxy_reply->vps;
407 } else if (strncasecmp(name, "control:", 8) == 0) {
409 vps = request->config_items;
410 } /* else it must be a bare attribute name */
416 dattr = dict_attrbyname(p);
418 fprintf(stderr, "No such attribute %s\n", p);
419 return NULL; /* no such attribute */
422 return pairfind(vps, dattr->attr);
427 * Evaluate an assignment
429 static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)
431 const policy_assignment_t *this;
432 const DICT_ATTR *dattr;
434 this = (const policy_assignment_t *) item;
436 rad_assert(this->lhs != NULL);
437 rad_assert(this->rhs != NULL);
440 dattr = dict_attrbyname(this->lhs);
442 fprintf(stderr, "HUH?\n");
452 * Evaluate a condition
454 static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
457 const policy_condition_t *this;
465 char lhs_buffer[2048];
467 this = (const policy_condition_t *) item;
471 * FIXME: Don't always do this...
473 if ((this->compare != POLICY_LEX_L_BRACKET) &&
474 (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING)) {
475 if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs,
476 state->request, NULL) > 0) {
481 switch (this->compare) {
482 case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */
483 rcode = evaluate_condition(state, this->child);
486 case POLICY_LEX_L_NOT:
487 rcode = evaluate_condition(state, this->child);
488 rcode = (rcode == FALSE); /* reverse sense of test */
491 case POLICY_LEX_CMP_TRUE: /* existence */
492 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
493 vp = find_vp(state->request, this->lhs);
494 rcode = (vp != NULL);
496 rcode = (data != NULL);
500 default: /* process other comparisons */
501 if ((this->compare != POLICY_LEX_CMP_EQUALS) &&
503 (this->compare != POLICY_LEX_RX_EQUALS) &&
504 (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&
506 (this->compare != POLICY_LEX_LT) &&
507 (this->compare != POLICY_LEX_GT) &&
508 (this->compare != POLICY_LEX_LE) &&
509 (this->compare != POLICY_LEX_GE) &&
510 (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) {
511 fprintf(stderr, "%d: bad comparison\n",
516 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
520 vp = find_vp(state->request, this->lhs);
522 * FIXME: Move sanity checks to
523 * post-parse code, so we don't do
524 * it on every packet.
527 vp_prints_value(buffer, sizeof(buffer), vp, 0);
528 myvp = pairmake(vp->name, this->rhs, T_OP_EQ);
531 myvp = pairmake(this->lhs, this->rhs, T_OP_EQ);
539 * FIXME: What to do about comparisons
540 * where vp doesn't exist? Right now,
541 * "simplepaircmp" returns -1, which is
542 * probably a bad idea. it should
543 * instead take an operator, a pointer to
544 * the comparison result, and return
545 * "true/false" for "comparions
546 * succeeded/failed", which are different
547 * error codes than "comparison is less
548 * than, equal to, or greater than zero".
550 compare = simplepaircmp(state->request,
556 * FIXME: Do something for RHS type?
558 printf("CMP %s %s\n", lhs_buffer, this->rhs);
559 compare = strcmp(lhs_buffer, this->rhs);
562 debug_evaluate("CONDITION COMPARE %d\n", compare);
564 switch (this->compare) {
565 case POLICY_LEX_CMP_EQUALS:
566 rcode = (compare == 0);
569 case POLICY_LEX_CMP_NOT_EQUALS:
570 rcode = (compare != 0);
574 rcode = (compare < 0);
578 rcode = (compare > 0);
582 rcode =(compare <= 0);
586 rcode = (compare >= 0);
590 case POLICY_LEX_RX_EQUALS:
591 { /* FIXME: copied from src/main/valuepair.c */
593 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
596 * Include substring matches.
598 if (regcomp(®, this->rhs,
599 REG_EXTENDED) != 0) {
602 rad_assert(data != NULL);
603 rcode = regexec(®, data,
604 REQUEST_MAX_REGEX + 1,
606 rcode = (rcode == 0);
610 * Add %{0}, %{1}, etc.
612 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
617 * Didn't match: delete old
618 * match, if it existed.
621 (rxmatch[i].rm_so == -1)) {
622 p = request_data_get(state->request, state->request,
623 REQUEST_DATA_REGEX | i);
637 * Copy substring into buffer.
640 data + rxmatch[i].rm_so,
641 rxmatch[i].rm_eo - rxmatch[i].rm_so);
642 rxbuffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
645 * Copy substring, and add it to
648 * Note that we don't check
649 * for out of memory, which is
650 * the only error we can get...
652 p = strdup(rxbuffer);
653 request_data_add(state->request,
655 REQUEST_DATA_REGEX | i,
662 case POLICY_LEX_RX_NOT_EQUALS:
663 regcomp(®, this->rhs, REG_EXTENDED|REG_NOSUB);
664 rad_assert(data != NULL);
665 rcode = regexec(®, data,
667 rcode = (rcode != 0);
670 #endif /* HAVE_REGEX_H */
674 } /* switch over comparison operators */
675 break; /* default from first switch over compare */
681 switch (this->child_condition) {
685 case POLICY_LEX_L_AND:
686 if (!rcode) return rcode; /* FALSE && x == FALSE */
689 case POLICY_LEX_L_OR:
690 if (rcode) return rcode; /* TRUE && x == TRUE */
697 this = (const policy_condition_t *) this->child;
700 return 1; /* should never reach here */
705 * Evaluate an 'if' statement
707 static int evaluate_if(policy_state_t *state, const policy_item_t *item)
710 const policy_if_t *this;
712 this = (const policy_if_t *) item;
715 * evaluate_condition calls itself recursively.
716 * We should probably allocate a new state, instead.
718 rcode = evaluate_condition(state, this->condition);
719 debug_evaluate("IF condition returned %s\n",
720 rcode ? "true" : "false");
722 rcode = policy_stack_push(state, this->if_true);
723 if (!rcode) return rcode;
724 } else if (this->if_false) {
725 rcode = policy_stack_push(state, this->if_false);
726 if (!rcode) return rcode;
730 * 'if' can fail, if the block it's processing fails.
737 * Make a VALUE_PAIR from a policy_assignment_t*
739 * The assignment operator has to be '='.
741 static VALUE_PAIR *assign2vp(REQUEST *request,
742 const policy_assignment_t *assign)
745 LRAD_TOKEN operator = T_OP_EQ;
746 const char *value = assign->rhs;
749 if ((assign->rhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) &&
750 (strchr(assign->rhs, '%') != NULL)) {
751 radius_xlat(buffer, sizeof(buffer), assign->rhs,
757 * This is crappy.. fix it.
759 switch (assign->assign) {
760 case POLICY_LEX_ASSIGN:
764 case POLICY_LEX_SET_EQUALS:
768 case POLICY_LEX_PLUS_EQUALS:
773 fprintf(stderr, "Expected '=' for operator, not '%s' at line %d\n",
774 lrad_int2str(rlm_policy_tokens,
775 assign->assign, "?"),
776 assign->item.lineno);
780 vp = pairmake(assign->lhs, value, operator);
782 fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);
790 * Evaluate a 'packet .= {attrs}' statement
792 static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
794 const policy_attributes_t *this;
795 VALUE_PAIR **vps = NULL;
796 VALUE_PAIR *vp, *head, **tail;
797 const policy_item_t *attr;
799 this = (const policy_attributes_t *) item;
801 switch (this->where) {
802 case POLICY_RESERVED_CONTROL:
803 vps = &(state->request->config_items);
806 case POLICY_RESERVED_REQUEST:
807 vps = &(state->request->packet->vps);
810 case POLICY_RESERVED_REPLY:
811 vps = &(state->request->reply->vps);
814 case POLICY_RESERVED_PROXY_REQUEST:
815 if (!state->request->proxy) return 0; /* FIXME: print error */
816 vps = &(state->request->proxy->vps);
819 case POLICY_RESERVED_PROXY_REPLY:
820 if (!state->request->proxy_reply) return 0; /* FIXME: print error */
821 vps = &(state->request->proxy_reply->vps);
831 for (attr = this->attributes; attr != NULL; attr = attr->next) {
832 if (attr->type != POLICY_TYPE_ASSIGNMENT) {
833 fprintf(stderr, "bad assignment in attribute list at line %d\n", attr->lineno);
838 vp = assign2vp(state->request, (const policy_assignment_t *) attr);
840 fprintf(stderr, "Failed to allocate VP\n");
849 case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
854 case POLICY_LEX_ASSIGN: /* 'union' */
855 pairmove(vps, &head);
859 case POLICY_LEX_CONCAT_EQUALS:
864 fprintf(stderr, "HUH?\n");
874 * Evaluate an 'call foo' statement
876 static int evaluate_call(policy_state_t *state, const policy_item_t *item)
879 const policy_call_t *this;
880 const policy_named_t *policy;
882 this = (const policy_call_t *) item;
884 policy = rlm_policy_find(state->inst->policies, this->name);
885 if (!policy) return 0; /* not found... */
887 DEBUG2("rlm_policy: Evaluating policy %s", this->name);
889 rad_assert(policy->policy->type != POLICY_TYPE_BAD);
890 rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);
893 * Push it onto the stack. Other code will take care of
896 rcode = policy_stack_push(state, policy->policy);
902 * Function calls always succeed?
904 * FIXME: Push the function name, etc. onto the stack,
905 * so we can check for infinite recursion above, and
906 * so we can also check for return codes from functions
914 * State machine stuff.
916 typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);
920 * MUST be kept in sync with policy_type_t
922 static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {
923 NULL, /* POLICY_TYPE_BAD */
929 NULL, /* define a named policy.. */
935 * Evaluate a policy, keyed by name.
937 static int policy_evaluate_name(policy_state_t *state, const char *name)
940 const policy_item_t *this;
941 policy_named_t mypolicy, *policy;
943 mypolicy.name = name;
944 policy = rbtree_finddata(state->inst->policies, &mypolicy);
945 if (!policy) return RLM_MODULE_FAIL;
947 DEBUG2("rlm_policy: Evaluating policy %s", name);
949 rad_assert(policy->item.type != POLICY_TYPE_BAD);
950 rad_assert(policy->item.type < POLICY_TYPE_NUM_TYPES);
952 rcode = policy_stack_push(state, policy->policy);
954 return RLM_MODULE_FAIL;
958 * FIXME: Look for magic keywords like "return",
959 * where the packet gets accepted/rejected/whatever
961 while (policy_stack_pop(state, &this)) {
962 rad_assert(this != NULL);
963 rad_assert(this->type != POLICY_TYPE_BAD);
964 rad_assert(this->type < POLICY_TYPE_NUM_TYPES);
966 debug_evaluate("Evaluating at line %d\n",
968 rcode = (*evaluate_functions[this->type])(state,
971 return RLM_MODULE_FAIL;
973 } /* loop until the stack is empty */
975 return RLM_MODULE_OK;
980 * Evaluate, which is pretty close to print, but we look at what
983 int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)
986 policy_state_t *state;
988 state = rad_malloc(sizeof(*state));
989 memset(state, 0, sizeof(*state));
990 state->request = request;
993 rcode = policy_evaluate_name(state, name);
997 return rcode; /* evaluated OK. */