2 * evaluate.c Evaluate a policy language
\r
6 * This program is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 2 of the License, or
\r
9 * (at your option) any later version.
\r
11 * This program is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program; if not, write to the Free Software
\r
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
20 * Copyright 2004 Alan DeKok <aland@ox.org>
\r
23 #include "rlm_policy.h"
\r
25 #include "modules.h"
\r
31 #define debug_evaluate if (0) printf
\r
34 * Print stuff we've parsed
\r
36 static void policy_print(const policy_item_t *item, int indent)
\r
39 if (indent) printf("%*s", indent, " ");
\r
45 switch (item->type) {
\r
46 case POLICY_TYPE_BAD:
\r
47 if (indent) printf("%*s", indent, " ");
\r
48 printf("[BAD STATEMENT]");
\r
51 case POLICY_TYPE_PRINT:
\r
52 if (indent) printf("%*s", indent, " ");
\r
54 const policy_print_t *this;
\r
56 this = (const policy_print_t *) item;
\r
58 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
\r
59 printf("print %s\n", this->rhs);
\r
61 printf("print \"%s\"\n", this->rhs);
\r
66 case POLICY_TYPE_ASSIGNMENT:
\r
68 const policy_assignment_t *assign;
\r
70 assign = (const policy_assignment_t *) item;
\r
71 if (indent) printf("%*s", indent, " ");
\r
73 printf("\t%s %s ", assign->lhs,
\r
74 lrad_int2str(rlm_policy_tokens,
\r
75 assign->assign, "?"));
\r
76 if (assign->rhs_type == POLICY_LEX_BARE_WORD) {
\r
77 printf("%s\n", assign->rhs);
\r
82 printf("\"%s\"\n", assign->rhs);
\r
87 case POLICY_TYPE_CONDITIONAL: /* no indentation here */
\r
89 const policy_condition_t *condition;
\r
91 condition = (const policy_condition_t *) item;
\r
96 * Nested conditions.
\r
98 if (condition->compare == POLICY_LEX_L_BRACKET) {
\r
99 policy_print(condition->child, indent);
\r
104 if (condition->compare == POLICY_LEX_L_NOT) {
\r
106 policy_print(condition->child, indent);
\r
111 if (condition->compare == POLICY_LEX_CMP_TRUE) {
\r
112 printf("%s)", condition->lhs);
\r
116 if (condition->lhs_type == POLICY_LEX_BARE_WORD) {
\r
117 printf("%s", condition->lhs);
\r
121 * and move all of this logic
\r
124 printf("\"%s\"", condition->lhs);
\r
128 * We always print this condition.
\r
130 printf(" %s ", lrad_int2str(rlm_policy_tokens,
\r
131 condition->compare,
\r
133 if (condition->rhs_type == POLICY_LEX_BARE_WORD) {
\r
134 printf("%s", condition->rhs);
\r
138 * and move all of this logic
\r
141 printf("\"%s\"", condition->rhs);
\r
145 if (condition->child_condition != POLICY_LEX_BAD) {
\r
146 printf(" %s ", lrad_int2str(rlm_policy_tokens, condition->child_condition, "?"));
\r
147 policy_print(condition->child, indent);
\r
152 case POLICY_TYPE_IF:
\r
154 const policy_if_t *statement;
\r
156 statement = (const policy_if_t *) item;
\r
158 if (indent) printf("%*s", indent, " ");
\r
160 policy_print(statement->condition, indent);
\r
162 policy_print(statement->if_true, indent + 1);
\r
163 if (indent) printf("%*s", indent, " ");
\r
164 if (statement->if_false) {
\r
166 if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {
\r
168 policy_print(statement->if_false, indent + 1);
\r
169 if (indent) printf("%*s", indent, " ");
\r
172 policy_print(statement->if_false, indent + 1);
\r
180 case POLICY_TYPE_ATTRIBUTE_LIST:
\r
182 const policy_attributes_t *this;
\r
184 this = (const policy_attributes_t *) item;
\r
186 if (indent) printf("%*s", indent, " ");
\r
187 printf("%s %s {\n",
\r
188 lrad_int2str(policy_reserved_words,
\r
190 lrad_int2str(rlm_policy_tokens,
\r
192 policy_print(this->attributes, indent + 1);
\r
193 if (indent) printf("%*s", indent, " ");
\r
198 case POLICY_TYPE_NAMED_POLICY:
\r
200 const policy_named_t *this;
\r
202 this = (const policy_named_t *) item;
\r
203 if (indent) printf("%*s", indent, " ");
\r
204 printf("policy %s {\n", this->name);
\r
205 policy_print(this->policy, indent + 1);
\r
206 if (indent) printf("%*s", indent, " ");
\r
211 case POLICY_TYPE_CALL:
\r
213 const policy_call_t *this;
\r
215 this = (const policy_call_t *) item;
\r
216 if (indent) printf("%*s", indent, " ");
\r
217 printf("call %s\n", this->name);
\r
222 if (indent) printf("%*s", indent, " ");
\r
223 printf("[HUH?]\n");
\r
233 void rlm_policy_print(const policy_item_t *item)
\r
235 printf("----------------------------------------------------------\n");
\r
236 policy_print(item, 0);
\r
237 printf("----------------------------------------------------------\n");
\r
241 * Internal stack of things to do.
\r
243 * When a function is about to be pushed onto the stack, we walk
\r
244 * backwards through the stack, and ensure that the function is
\r
245 * not already there. This prevents infinite recursion.
\r
247 * This means that we NEVER pop functions. Rather, we push the
\r
248 * function, and then immediately push it's first element.
\r
250 * When we've finished popping all of the elements, we pop the
\r
251 * function, realize it's a function, ignore it, and pop one more
\r
254 #define POLICY_MAX_STACK 16
\r
255 typedef struct policy_state_t {
\r
256 rlm_policy_t *inst;
\r
258 REQUEST *request; /* so it's not passed on the C stack */
\r
259 const policy_item_t *stack[POLICY_MAX_STACK];
\r
264 * Push an item onto the state.
\r
266 static int policy_stack_push(policy_state_t *state, const policy_item_t *item)
\r
268 rad_assert(state->depth >= 0);
\r
271 * Asked to push nothing. Don't push it.
\r
273 if (!item) return 1;
\r
276 * State is full. Die.
\r
278 if (state->depth >= POLICY_MAX_STACK) {
\r
283 * Walk back up the stack, looking for previous ocurrances
\r
284 * of this name. If found, we have infinite recursion,
\r
285 * which we stop dead in the water!
\r
287 if (item->type == POLICY_TYPE_NAMED_POLICY) {
\r
290 for (i = 0; i < state->depth; i++) {
\r
291 if (state->stack[i]->type != POLICY_TYPE_NAMED_POLICY) {
\r
296 * FIXME: check for more stuff.
\r
301 debug_evaluate("push %d %p\n", state->depth, item);
\r
303 state->stack[state->depth] = item;
\r
304 state->depth++; /* points to unused entry */
\r
311 * Pop an item from the state.
\r
313 static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)
\r
315 rad_assert(pitem != NULL);
\r
316 rad_assert(state->depth >= 0);
\r
318 if (state->depth == 0) {
\r
323 *pitem = state->stack[state->depth - 1];
\r
326 * Process the whole item list.
\r
328 if ((*pitem)->next) {
\r
329 state->stack[state->depth - 1] = (*pitem)->next;
\r
330 debug_evaluate("pop/push %d %p\n", state->depth - 1, *pitem);
\r
332 state->depth--; /* points to unused entry */
\r
333 debug_evaluate("pop %d %p\n", state->depth, *pitem);
\r
341 * Evaluate a print statement
\r
343 static int evaluate_print(policy_state_t *state, const policy_item_t *item)
\r
345 const policy_print_t *this;
\r
347 this = (const policy_print_t *) item;
\r
349 if (this->rhs_type == POLICY_LEX_BARE_WORD) {
\r
350 printf("%s\n", this->rhs);
\r
354 radius_xlat(buffer, sizeof(buffer), this->rhs,
\r
355 state->request, NULL);
\r
356 printf("%s", buffer);
\r
363 * Return a VALUE_PAIR, given an attribute name.
\r
365 * FIXME: Have it return the N'th one, too, like
\r
366 * doc/variables.txt?
\r
368 * The amount of duplicated code is getting annoying...
\r
370 static VALUE_PAIR *find_vp(REQUEST *request, const char *name)
\r
373 const DICT_ATTR *dattr;
\r
377 vps = request->packet->vps;;
\r
380 * FIXME: use names from reserved word list?
\r
382 if (strncasecmp(name, "request:", 8) == 0) {
\r
384 } else if (strncasecmp(name, "reply:", 6) == 0) {
\r
386 vps = request->reply->vps;
\r
387 } else if (strncasecmp(name, "proxy-request:", 14) == 0) {
\r
389 if (request->proxy) {
\r
390 vps = request->proxy->vps;
\r
392 } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {
\r
394 if (request->proxy_reply) {
\r
395 vps = request->proxy_reply->vps;
\r
397 } else if (strncasecmp(name, "control:", 8) == 0) {
\r
399 vps = request->config_items;
\r
400 } /* else it must be a bare attribute name */
\r
406 dattr = dict_attrbyname(p);
\r
408 fprintf(stderr, "No such attribute %s\n", p);
\r
409 return NULL; /* no such attribute */
\r
412 return pairfind(vps, dattr->attr);
\r
417 * Evaluate an assignment
\r
419 static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)
\r
421 const policy_assignment_t *this;
\r
422 const DICT_ATTR *dattr;
\r
424 this = (const policy_assignment_t *) item;
\r
426 rad_assert(this->lhs != NULL);
\r
427 rad_assert(this->rhs != NULL);
\r
430 dattr = dict_attrbyname(this->lhs);
\r
432 fprintf(stderr, "HUH?\n");
\r
442 * Evaluate a condition
\r
444 static int evaluate_condition(policy_state_t *state, const policy_item_t *item)
\r
447 const policy_condition_t *this;
\r
451 #ifdef HAVE_REGEX_H
\r
455 char lhs_buffer[2048];
\r
457 this = (const policy_condition_t *) item;
\r
461 * FIXME: Don't always do this...
\r
463 if ((this->compare != POLICY_LEX_L_BRACKET) &&
\r
464 (this->lhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING)) {
\r
465 if (radius_xlat(lhs_buffer, sizeof(lhs_buffer), this->lhs,
\r
466 state->request, NULL) > 0) {
\r
471 switch (this->compare) {
\r
472 case POLICY_LEX_L_BRACKET: /* nested brackets are a special case */
\r
473 rcode = evaluate_condition(state, this->child);
\r
476 case POLICY_LEX_L_NOT:
\r
477 rcode = evaluate_condition(state, this->child);
\r
478 rcode = (rcode == FALSE); /* reverse sense of test */
\r
481 case POLICY_LEX_CMP_TRUE: /* existence */
\r
482 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
\r
483 vp = find_vp(state->request, this->lhs);
\r
484 rcode = (vp != NULL);
\r
486 rcode = (data != NULL);
\r
490 default: /* process other comparisons */
\r
491 if ((this->compare != POLICY_LEX_CMP_EQUALS) &&
\r
492 #ifdef HAVE_REGEX_H
\r
493 (this->compare != POLICY_LEX_RX_EQUALS) &&
\r
494 (this->compare != POLICY_LEX_RX_NOT_EQUALS) &&
\r
496 (this->compare != POLICY_LEX_LT) &&
\r
497 (this->compare != POLICY_LEX_GT) &&
\r
498 (this->compare != POLICY_LEX_LE) &&
\r
499 (this->compare != POLICY_LEX_GE) &&
\r
500 (this->compare != POLICY_LEX_CMP_NOT_EQUALS)) {
\r
501 fprintf(stderr, "%d: bad comparison\n",
\r
502 this->item.lineno);
\r
506 if (this->lhs_type == POLICY_LEX_BARE_WORD) {
\r
510 vp = find_vp(state->request, this->lhs);
\r
512 * FIXME: Move sanity checks to
\r
513 * post-parse code, so we don't do
\r
514 * it on every packet.
\r
517 vp_prints_value(buffer, sizeof(buffer), vp, 0);
\r
518 myvp = pairmake(vp->name, this->rhs, T_OP_EQ);
\r
521 myvp = pairmake(this->lhs, this->rhs, T_OP_EQ);
\r
529 * FIXME: What to do about comparisons
\r
530 * where vp doesn't exist? Right now,
\r
531 * "simplepaircmp" returns -1, which is
\r
532 * probably a bad idea. it should
\r
533 * instead take an operator, a pointer to
\r
534 * the comparison result, and return
\r
535 * "true/false" for "comparions
\r
536 * succeeded/failed", which are different
\r
537 * error codes than "comparison is less
\r
538 * than, equal to, or greater than zero".
\r
540 compare = simplepaircmp(state->request,
\r
546 * FIXME: Do something for RHS type?
\r
548 printf("CMP %s %s\n", lhs_buffer, this->rhs);
\r
549 compare = strcmp(lhs_buffer, this->rhs);
\r
552 debug_evaluate("CONDITION COMPARE %d\n", compare);
\r
554 switch (this->compare) {
\r
555 case POLICY_LEX_CMP_EQUALS:
\r
556 rcode = (compare == 0);
\r
559 case POLICY_LEX_CMP_NOT_EQUALS:
\r
560 rcode = (compare != 0);
\r
563 case POLICY_LEX_LT:
\r
564 rcode = (compare < 0);
\r
567 case POLICY_LEX_GT:
\r
568 rcode = (compare > 0);
\r
571 case POLICY_LEX_LE:
\r
572 rcode =(compare <= 0);
\r
575 case POLICY_LEX_GE:
\r
576 rcode = (compare >= 0);
\r
579 #ifdef HAVE_REGEX_H
\r
580 case POLICY_LEX_RX_EQUALS:
\r
581 { /* FIXME: copied from src/main/valuepair.c */
\r
583 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
\r
586 * Include substring matches.
\r
588 if (regcomp(®, this->rhs,
\r
589 REG_EXTENDED) != 0) {
\r
592 rad_assert(data != NULL);
\r
593 rcode = regexec(®, data,
\r
594 REQUEST_MAX_REGEX + 1,
\r
596 rcode = (rcode == 0);
\r
600 * Add %{0}, %{1}, etc.
\r
602 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
\r
604 char rxbuffer[256];
\r
607 * Didn't match: delete old
\r
608 * match, if it existed.
\r
611 (rxmatch[i].rm_so == -1)) {
\r
612 p = request_data_get(state->request, state->request,
\r
613 REQUEST_DATA_REGEX | i);
\r
620 * No previous match
\r
627 * Copy substring into buffer.
\r
630 data + rxmatch[i].rm_so,
\r
631 rxmatch[i].rm_eo - rxmatch[i].rm_so);
\r
632 rxbuffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
\r
635 * Copy substring, and add it to
\r
638 * Note that we don't check
\r
639 * for out of memory, which is
\r
640 * the only error we can get...
\r
642 p = strdup(rxbuffer);
\r
643 request_data_add(state->request,
\r
645 REQUEST_DATA_REGEX | i,
\r
652 case POLICY_LEX_RX_NOT_EQUALS:
\r
653 regcomp(®, this->rhs, REG_EXTENDED|REG_NOSUB);
\r
654 rad_assert(data != NULL);
\r
655 rcode = regexec(®, data,
\r
657 rcode = (rcode != 0);
\r
660 #endif /* HAVE_REGEX_H */
\r
664 } /* switch over comparison operators */
\r
665 break; /* default from first switch over compare */
\r
669 * No trailing &&, ||
\r
671 switch (this->child_condition) {
\r
675 case POLICY_LEX_L_AND:
\r
676 if (!rcode) return rcode; /* FALSE && x == FALSE */
\r
679 case POLICY_LEX_L_OR:
\r
680 if (rcode) return rcode; /* TRUE && x == TRUE */
\r
687 this = (const policy_condition_t *) this->child;
\r
690 return 1; /* should never reach here */
\r
695 * Evaluate an 'if' statement
\r
697 static int evaluate_if(policy_state_t *state, const policy_item_t *item)
\r
700 const policy_if_t *this;
\r
702 this = (const policy_if_t *) item;
\r
705 * evaluate_condition calls itself recursively.
\r
706 * We should probably allocate a new state, instead.
\r
708 rcode = evaluate_condition(state, this->condition);
\r
709 debug_evaluate("IF condition returned %s\n",
\r
710 rcode ? "true" : "false");
\r
712 rcode = policy_stack_push(state, this->if_true);
\r
713 if (!rcode) return rcode;
\r
714 } else if (this->if_false) {
\r
715 rcode = policy_stack_push(state, this->if_false);
\r
716 if (!rcode) return rcode;
\r
720 * 'if' can fail, if the block it's processing fails.
\r
727 * Make a VALUE_PAIR from a policy_assignment_t*
\r
729 * The assignment operator has to be '='.
\r
731 static VALUE_PAIR *assign2vp(REQUEST *request,
\r
732 const policy_assignment_t *assign)
\r
735 LRAD_TOKEN operator = T_OP_EQ;
\r
736 const char *value = assign->rhs;
\r
739 if ((assign->rhs_type == POLICY_LEX_DOUBLE_QUOTED_STRING) &&
\r
740 (strchr(assign->rhs, '%') != NULL)) {
\r
741 radius_xlat(buffer, sizeof(buffer), assign->rhs,
\r
747 * This is crappy.. fix it.
\r
749 switch (assign->assign) {
\r
750 case POLICY_LEX_ASSIGN:
\r
751 operator = T_OP_EQ;
\r
754 case POLICY_LEX_SET_EQUALS:
\r
755 operator = T_OP_SET;
\r
758 case POLICY_LEX_PLUS_EQUALS:
\r
759 operator = T_OP_ADD;
\r
763 fprintf(stderr, "Expected '=' for operator, not '%s' at line %d\n",
\r
764 lrad_int2str(rlm_policy_tokens,
\r
765 assign->assign, "?"),
\r
766 assign->item.lineno);
\r
770 vp = pairmake(assign->lhs, value, operator);
\r
772 fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);
\r
780 * Evaluate a 'packet .= {attrs}' statement
\r
782 static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)
\r
784 const policy_attributes_t *this;
\r
785 VALUE_PAIR **vps = NULL;
\r
786 VALUE_PAIR *vp, *head, **tail;
\r
787 const policy_item_t *attr;
\r
789 this = (const policy_attributes_t *) item;
\r
791 switch (this->where) {
\r
792 case POLICY_RESERVED_CONTROL:
\r
793 vps = &(state->request->config_items);
\r
796 case POLICY_RESERVED_REQUEST:
\r
797 vps = &(state->request->packet->vps);
\r
800 case POLICY_RESERVED_REPLY:
\r
801 vps = &(state->request->reply->vps);
\r
804 case POLICY_RESERVED_PROXY_REQUEST:
\r
805 if (!state->request->proxy) return 0; /* FIXME: print error */
\r
806 vps = &(state->request->proxy->vps);
\r
809 case POLICY_RESERVED_PROXY_REPLY:
\r
810 if (!state->request->proxy_reply) return 0; /* FIXME: print error */
\r
811 vps = &(state->request->proxy_reply->vps);
\r
821 for (attr = this->attributes; attr != NULL; attr = attr->next) {
\r
822 if (attr->type != POLICY_TYPE_ASSIGNMENT) {
\r
823 fprintf(stderr, "bad assignment in attribute list at line %d\n", attr->lineno);
\r
828 vp = assign2vp(state->request, (const policy_assignment_t *) attr);
\r
830 fprintf(stderr, "Failed to allocate VP\n");
\r
835 tail = &(vp->next);
\r
838 switch (this->how) {
\r
839 case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
\r
844 case POLICY_LEX_ASSIGN: /* 'union' */
\r
845 pairmove(vps, &head);
\r
849 case POLICY_LEX_CONCAT_EQUALS:
\r
850 pairadd(vps, head);
\r
854 fprintf(stderr, "HUH?\n");
\r
864 * Evaluate an 'call foo' statement
\r
866 static int evaluate_call(policy_state_t *state, const policy_item_t *item)
\r
869 const policy_call_t *this;
\r
870 const rlm_policy_name_t *policy;
\r
872 this = (const policy_call_t *) item;
\r
874 policy = rlm_policy_find(state->inst->policies, this->name);
\r
875 if (!policy) return 0; /* not found... */
\r
877 DEBUG2("rlm_policy: Evaluating policy %s", this->name);
\r
879 rad_assert(policy->policy->type != POLICY_TYPE_BAD);
\r
880 rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);
\r
883 * Push it onto the stack. Other code will take care of
\r
886 rcode = policy_stack_push(state, policy->policy);
\r
892 * Function calls always succeed?
\r
894 * FIXME: Push the function name, etc. onto the stack,
\r
895 * so we can check for infinite recursion above, and
\r
896 * so we can also check for return codes from functions
\r
904 * State machine stuff.
\r
906 typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);
\r
910 * MUST be kept in sync with policy_type_t
\r
912 static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {
\r
913 NULL, /* POLICY_TYPE_BAD */
\r
915 evaluate_condition,
\r
916 evaluate_assignment,
\r
917 evaluate_attr_list,
\r
919 NULL, /* define a named policy.. */
\r
925 * Evaluate a policy, keyed by name.
\r
927 static int policy_evaluate_name(policy_state_t *state, const char *name)
\r
930 const policy_item_t *this;
\r
931 rlm_policy_name_t mypolicy, *policy;
\r
933 strNcpy(mypolicy.name, name, sizeof(mypolicy.name));
\r
934 policy = rbtree_finddata(state->inst->policies, &mypolicy);
\r
935 if (!policy) return RLM_MODULE_FAIL;
\r
937 DEBUG2("rlm_policy: Evaluating policy %s", name);
\r
939 rad_assert(policy->policy->type != POLICY_TYPE_BAD);
\r
940 rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);
\r
942 rcode = policy_stack_push(state, policy->policy);
\r
944 return RLM_MODULE_FAIL;
\r
948 * FIXME: Look for magic keywords like "return",
\r
949 * where the packet gets accepted/rejected/whatever
\r
951 while (policy_stack_pop(state, &this)) {
\r
952 rad_assert(this != NULL);
\r
953 rad_assert(this->type != POLICY_TYPE_BAD);
\r
954 rad_assert(this->type < POLICY_TYPE_NUM_TYPES);
\r
956 debug_evaluate("Evaluating at line %d\n",
\r
958 rcode = (*evaluate_functions[this->type])(state,
\r
961 return RLM_MODULE_FAIL;
\r
963 } /* loop until the stack is empty */
\r
965 return RLM_MODULE_OK;
\r
970 * Evaluate, which is pretty close to print, but we look at what
\r
973 int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)
\r
976 policy_state_t *state;
\r
978 state = rad_malloc(sizeof(*state));
\r
979 memset(state, 0, sizeof(*state));
\r
980 state->request = request;
\r
981 state->inst = inst;
\r
983 rcode = policy_evaluate_name(state, name);
\r
987 return rcode; /* evaluated OK. */
\r