Calling policies is now done by foo(), not by "call foo"
[freeradius.git] / src / modules / rlm_policy / evaluate.c
1 /*\r
2  * evaluate.c           Evaluate a policy language\r
3  *\r
4  * Version:     $Id$\r
5  *\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
10  *\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
15  *\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
19  *\r
20  * Copyright 2004  Alan DeKok <aland@ox.org>\r
21  */\r
22 \r
23 #include "rlm_policy.h"\r
24 \r
25 #include "modules.h"\r
26 \r
27 #ifdef HAVE_REGEX_H\r
28 #include <regex.h>\r
29 #endif\r
30 \r
31 #define debug_evaluate if (0) printf\r
32 \r
33 /*\r
34  *      Print stuff we've parsed\r
35  */\r
36 static void policy_print(const policy_item_t *item, int indent)\r
37 {\r
38         if (!item) {\r
39                 if (indent) printf("%*s", indent, " ");\r
40                 printf("[NULL]\n");\r
41                 return;\r
42         }\r
43         \r
44         while (item) {\r
45                 switch (item->type) {\r
46                 case POLICY_TYPE_BAD:\r
47                         if (indent) printf("%*s", indent, " ");\r
48                         printf("[BAD STATEMENT]");\r
49                         break;\r
50                         \r
51                 case POLICY_TYPE_PRINT:\r
52                         if (indent) printf("%*s", indent, " ");\r
53                         {\r
54                                 const policy_print_t *this;\r
55 \r
56                                 this = (const policy_print_t *) item;\r
57                                 \r
58                                 if (this->rhs_type == POLICY_LEX_BARE_WORD) {\r
59                                         printf("print %s\n", this->rhs);\r
60                                 } else {\r
61                                         printf("print \"%s\"\n", this->rhs);\r
62                                 }\r
63                         }\r
64                         break;\r
65                         \r
66                 case POLICY_TYPE_ASSIGNMENT:\r
67                         {\r
68                                 const policy_assignment_t *assign;\r
69                                 \r
70                                 assign = (const policy_assignment_t *) item;\r
71                                 if (indent) printf("%*s", indent, " ");\r
72 \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
78                                 } else {\r
79                                         /*\r
80                                          *      FIXME: escape "\r
81                                          */\r
82                                         printf("\"%s\"\n", assign->rhs);\r
83                                 }\r
84                         }\r
85                         break;\r
86 \r
87                 case POLICY_TYPE_CONDITIONAL: /* no indentation here */\r
88                         {\r
89                                 const policy_condition_t *condition;\r
90 \r
91                                 condition = (const policy_condition_t *) item;\r
92 \r
93                                 printf("(");\r
94 \r
95                                 /*\r
96                                  *      Nested conditions.\r
97                                  */\r
98                                 if (condition->compare == POLICY_LEX_L_BRACKET) {\r
99                                         policy_print(condition->child, indent);\r
100                                         printf(")");\r
101                                         break;\r
102                                 }\r
103 \r
104                                 if (condition->compare == POLICY_LEX_L_NOT) {\r
105                                         printf("!");\r
106                                         policy_print(condition->child, indent);\r
107                                         printf(")");\r
108                                         break;\r
109                                 }\r
110 \r
111                                 if (condition->compare == POLICY_LEX_CMP_TRUE) {\r
112                                         printf("%s)", condition->lhs);\r
113                                         break;\r
114                                 }\r
115 \r
116                                 if (condition->lhs_type == POLICY_LEX_BARE_WORD) {\r
117                                         printf("%s", condition->lhs);\r
118                                 } else {\r
119                                         /*\r
120                                          *      FIXME: escape ",\r
121                                          *      and move all of this logic\r
122                                          *      to a function.\r
123                                          */\r
124                                         printf("\"%s\"", condition->lhs);\r
125                                 }\r
126 \r
127                                 /*\r
128                                  *      We always print this condition.\r
129                                  */\r
130                                 printf(" %s ", lrad_int2str(rlm_policy_tokens,\r
131                                                             condition->compare,\r
132                                                             "?"));\r
133                                 if (condition->rhs_type == POLICY_LEX_BARE_WORD) {\r
134                                         printf("%s", condition->rhs);\r
135                                 } else {\r
136                                         /*\r
137                                          *      FIXME: escape ",\r
138                                          *      and move all of this logic\r
139                                          *      to a function.\r
140                                          */\r
141                                         printf("\"%s\"", condition->rhs);\r
142                                 }\r
143                                 printf(")");\r
144                                 \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
148                                 }\r
149                         }\r
150                         break;\r
151 \r
152                 case POLICY_TYPE_IF:\r
153                         {\r
154                                 const policy_if_t *statement;\r
155 \r
156                                 statement = (const policy_if_t *) item;\r
157 \r
158                                 if (indent) printf("%*s", indent, " ");\r
159                                 printf("if ");\r
160                                 policy_print(statement->condition, indent);\r
161                                 printf(" {\n");\r
162                                 policy_print(statement->if_true, indent + 1);\r
163                                 if (indent) printf("%*s", indent, " ");\r
164                                 if (statement->if_false) {\r
165                                         printf("} else ");\r
166                                         if (statement->if_false->type == POLICY_TYPE_ASSIGNMENT) {\r
167                                                 printf(" { ");\r
168                                                 policy_print(statement->if_false, indent + 1);\r
169                                                 if (indent) printf("%*s", indent, " ");\r
170                                                 printf(" }");\r
171                                         } else {\r
172                                                 policy_print(statement->if_false, indent + 1);\r
173                                         }\r
174                                 } else {\r
175                                         printf("}\n");\r
176                                 }\r
177                         }\r
178                         break;\r
179 \r
180                 case POLICY_TYPE_ATTRIBUTE_LIST:\r
181                         {\r
182                                 const policy_attributes_t *this;\r
183 \r
184                                 this = (const policy_attributes_t *) item;\r
185 \r
186                                 if (indent) printf("%*s", indent, " ");\r
187                                 printf("%s %s {\n",\r
188                                        lrad_int2str(policy_reserved_words,\r
189                                                     this->where, "?"),\r
190                                        lrad_int2str(rlm_policy_tokens,\r
191                                                     this->how, "?"));\r
192                                 policy_print(this->attributes, indent + 1);\r
193                                 if (indent) printf("%*s", indent, " ");\r
194                                 printf("}\n");\r
195                         }\r
196                         break;\r
197 \r
198                 case POLICY_TYPE_NAMED_POLICY:\r
199                         {\r
200                                 const policy_named_t *this;\r
201 \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
207                                 printf("}\n");\r
208                         }\r
209                         break;\r
210 \r
211                 case POLICY_TYPE_CALL:\r
212                         {\r
213                                 const policy_call_t *this;\r
214 \r
215                                 this = (const policy_call_t *) item;\r
216                                 if (indent) printf("%*s", indent, " ");\r
217                                 printf("call %s\n", this->name);\r
218                         }\r
219                         break;\r
220 \r
221                 default:\r
222                         if (indent) printf("%*s", indent, " ");\r
223                         printf("[HUH?]\n");\r
224                         break;\r
225                         \r
226                 }\r
227 \r
228                 item = item->next;\r
229         }\r
230 }\r
231 \r
232 \r
233 void rlm_policy_print(const policy_item_t *item)\r
234 {\r
235         printf("----------------------------------------------------------\n");\r
236         policy_print(item, 0);\r
237         printf("----------------------------------------------------------\n");\r
238 }\r
239 \r
240 /*\r
241  *      Internal stack of things to do.\r
242  *\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
246  *\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
249  *\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
252  *      entry.\r
253  */\r
254 #define POLICY_MAX_STACK 16\r
255 typedef struct policy_state_t {\r
256         rlm_policy_t    *inst;\r
257         int             depth;\r
258         REQUEST         *request; /* so it's not passed on the C stack */\r
259         const policy_item_t *stack[POLICY_MAX_STACK];\r
260 } policy_state_t;\r
261 \r
262 \r
263 /*\r
264  *      Push an item onto the state.\r
265  */\r
266 static int policy_stack_push(policy_state_t *state, const policy_item_t *item)\r
267 {\r
268         rad_assert(state->depth >= 0);\r
269 \r
270         /*\r
271          *      Asked to push nothing.  Don't push it.\r
272          */\r
273         if (!item) return 1;\r
274 \r
275         /*\r
276          *      State is full.  Die.\r
277          */\r
278         if (state->depth >= POLICY_MAX_STACK) {\r
279                 return 0;\r
280         }\r
281 \r
282         /*\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
286          */\r
287         if (item->type == POLICY_TYPE_NAMED_POLICY) {\r
288                 int i;\r
289 \r
290                 for (i = 0; i < state->depth; i++) {\r
291                         if (state->stack[i]->type != POLICY_TYPE_NAMED_POLICY) {\r
292                                 continue;\r
293                         }\r
294 \r
295                         /*\r
296                          *      FIXME: check for more stuff.\r
297                          */\r
298                 }\r
299         }\r
300 \r
301         debug_evaluate("push %d %p\n", state->depth, item);\r
302 \r
303         state->stack[state->depth] = item;\r
304         state->depth++;         /* points to unused entry */\r
305 \r
306         return 1;\r
307 }\r
308 \r
309 \r
310 /*\r
311  *      Pop an item from the state.\r
312  */\r
313 static int policy_stack_pop(policy_state_t *state, const policy_item_t **pitem)\r
314 {\r
315         rad_assert(pitem != NULL);\r
316         rad_assert(state->depth >= 0);\r
317 \r
318         if (state->depth == 0) {\r
319                 *pitem = NULL;\r
320                 return 0;\r
321         }\r
322 \r
323         *pitem = state->stack[state->depth - 1];\r
324 \r
325         /*\r
326          *      Process the whole item list.\r
327          */\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
331         } else {\r
332                 state->depth--;         /* points to unused entry */\r
333                 debug_evaluate("pop %d %p\n", state->depth, *pitem);\r
334         }\r
335 \r
336         return 1;\r
337 }\r
338 \r
339 \r
340 /*\r
341  *      Evaluate a print statement\r
342  */\r
343 static int evaluate_print(policy_state_t *state, const policy_item_t *item)\r
344 {\r
345         const policy_print_t *this;\r
346 \r
347         this = (const policy_print_t *) item;\r
348 \r
349         if (this->rhs_type == POLICY_LEX_BARE_WORD) {\r
350                 printf("%s\n", this->rhs);\r
351         } else {\r
352                 char buffer[1024];\r
353 \r
354                 radius_xlat(buffer, sizeof(buffer), this->rhs,\r
355                             state->request, NULL);\r
356                 printf("%s", buffer);\r
357         }\r
358 \r
359         return 1;\r
360 }\r
361 \r
362 /*\r
363  *      Return a VALUE_PAIR, given an attribute name.\r
364  *\r
365  *      FIXME: Have it return the N'th one, too, like\r
366  *      doc/variables.txt?\r
367  *\r
368  *      The amount of duplicated code is getting annoying...\r
369  */\r
370 static VALUE_PAIR *find_vp(REQUEST *request, const char *name)\r
371 {\r
372         const char *p;\r
373         const DICT_ATTR *dattr;\r
374         VALUE_PAIR *vps;\r
375 \r
376         p = name;\r
377         vps = request->packet->vps;;\r
378 \r
379         /*\r
380          *      FIXME: use names from reserved word list?\r
381          */\r
382         if (strncasecmp(name, "request:", 8) == 0) {\r
383                 p += 8;\r
384         } else if (strncasecmp(name, "reply:", 6) == 0) {\r
385                 p += 6;\r
386                 vps = request->reply->vps;\r
387         } else if (strncasecmp(name, "proxy-request:", 14) == 0) {\r
388                 p += 14;\r
389                 if (request->proxy) {\r
390                         vps = request->proxy->vps;\r
391                 }\r
392         } else if (strncasecmp(name, "proxy-reply:", 12) == 0) {\r
393                 p += 12;\r
394                 if (request->proxy_reply) {\r
395                         vps = request->proxy_reply->vps;\r
396                 }\r
397         } else if (strncasecmp(name, "control:", 8) == 0) {\r
398                 p += 8;\r
399                 vps = request->config_items;\r
400         } /* else it must be a bare attribute name */\r
401 \r
402         if (!vps) {\r
403                 return NULL;\r
404         }\r
405 \r
406         dattr = dict_attrbyname(p);\r
407         if (!dattr) {\r
408                 fprintf(stderr, "No such attribute %s\n", p);\r
409                 return NULL;    /* no such attribute */\r
410         }\r
411 \r
412         return pairfind(vps, dattr->attr);\r
413 }\r
414 \r
415 \r
416 /*\r
417  *      Evaluate an assignment\r
418  */\r
419 static int evaluate_assignment(policy_state_t *state, const policy_item_t *item)\r
420 {\r
421         const policy_assignment_t *this;\r
422         const DICT_ATTR *dattr;\r
423 \r
424         this = (const policy_assignment_t *) item;\r
425 \r
426         rad_assert(this->lhs != NULL);\r
427         rad_assert(this->rhs != NULL);\r
428 \r
429 #if 0\r
430         dattr = dict_attrbyname(this->lhs);\r
431         if (!dattr) {\r
432                 fprintf(stderr, "HUH?\n");\r
433                 return 0;\r
434         }\r
435 #endif\r
436 \r
437         return 1;\r
438 }\r
439 \r
440 \r
441 /*\r
442  *      Evaluate a condition\r
443  */\r
444 static int evaluate_condition(policy_state_t *state, const policy_item_t *item)\r
445 {\r
446         int rcode;\r
447         const policy_condition_t *this;\r
448         VALUE_PAIR *vp;\r
449         char *data = NULL;\r
450         int compare;\r
451 #ifdef HAVE_REGEX_H\r
452         regex_t reg;\r
453 #endif\r
454         char buffer[256];\r
455         char lhs_buffer[2048];\r
456 \r
457         this = (const policy_condition_t *) item;\r
458 \r
459  redo:\r
460         /*\r
461          *      FIXME: Don't always do this...\r
462          */\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
467                         data = lhs_buffer;\r
468                 }\r
469         }\r
470         \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
474                 break;\r
475 \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
479                 break;\r
480 \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
485                 } else {\r
486                         rcode = (data != NULL);\r
487                 }\r
488                 break;\r
489 \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
495 #endif\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
503                         return FALSE;\r
504                 }\r
505 \r
506                 if (this->lhs_type == POLICY_LEX_BARE_WORD) {\r
507                         VALUE_PAIR *myvp;\r
508 \r
509 \r
510                         vp = find_vp(state->request, this->lhs);\r
511                         /*\r
512                          *      FIXME: Move sanity checks to\r
513                          *      post-parse code, so we don't do\r
514                          *      it on every packet.\r
515                          */\r
516                         if (vp) {\r
517                                 vp_prints_value(buffer, sizeof(buffer), vp, 0);\r
518                                 myvp = pairmake(vp->name, this->rhs, T_OP_EQ);\r
519                         } else {\r
520                                 buffer[0] = '\0';\r
521                                 myvp = pairmake(this->lhs, this->rhs, T_OP_EQ);\r
522                         }\r
523                         data = buffer;\r
524                         if (!myvp) {\r
525                                 return FALSE;\r
526                         }\r
527 \r
528                         /*\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
539                          */\r
540                         compare = simplepaircmp(state->request,\r
541                                                 vp, myvp);\r
542                         pairfree(&myvp);\r
543                         \r
544                 } else {\r
545                         /*\r
546                          *      FIXME: Do something for RHS type?\r
547                          */\r
548                         printf("CMP %s %s\n", lhs_buffer, this->rhs);\r
549                         compare = strcmp(lhs_buffer, this->rhs);\r
550                 }\r
551 \r
552                 debug_evaluate("CONDITION COMPARE %d\n", compare);\r
553                 \r
554                 switch (this->compare) {\r
555                 case POLICY_LEX_CMP_EQUALS:\r
556                         rcode = (compare == 0);\r
557                         break;\r
558                         \r
559                 case POLICY_LEX_CMP_NOT_EQUALS:\r
560                         rcode = (compare != 0);\r
561                         break;\r
562                         \r
563                 case POLICY_LEX_LT:\r
564                         rcode = (compare < 0);\r
565                         break;\r
566                         \r
567                 case POLICY_LEX_GT:\r
568                         rcode = (compare > 0);\r
569                         break;\r
570                         \r
571                 case POLICY_LEX_LE:\r
572                         rcode =(compare <= 0);\r
573                         break;\r
574                         \r
575                 case POLICY_LEX_GE:\r
576                         rcode = (compare >= 0);\r
577                         break;\r
578                         \r
579 #ifdef HAVE_REGEX_H\r
580                 case POLICY_LEX_RX_EQUALS:\r
581                 { /* FIXME: copied from src/main/valuepair.c */\r
582                         int i;\r
583                         regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];\r
584                         \r
585                         /*\r
586                          *      Include substring matches.\r
587                          */\r
588                         if (regcomp(&reg, this->rhs,\r
589                                     REG_EXTENDED) != 0) {\r
590                                 return FALSE;\r
591                         }\r
592                         rad_assert(data != NULL);\r
593                         rcode = regexec(&reg, data,\r
594                                         REQUEST_MAX_REGEX + 1,\r
595                                         rxmatch, 0);\r
596                         rcode = (rcode == 0);\r
597                         regfree(&reg);\r
598                         \r
599                         /*\r
600                          *      Add %{0}, %{1}, etc.\r
601                          */\r
602                         for (i = 0; i <= REQUEST_MAX_REGEX; i++) {\r
603                                 char *p;\r
604                                 char rxbuffer[256];\r
605                                 \r
606                                 /*\r
607                                  *      Didn't match: delete old\r
608                                  *      match, if it existed.\r
609                                  */\r
610                                 if (!rcode ||\r
611                                     (rxmatch[i].rm_so == -1)) {\r
612                                         p = request_data_get(state->request, state->request,\r
613                                                              REQUEST_DATA_REGEX | i);\r
614                                         if (p) {\r
615                                                 free(p);\r
616                                                 continue;\r
617                                         }\r
618                                                 \r
619                                         /*\r
620                                          *      No previous match\r
621                                          *      to delete, stop.\r
622                                          */\r
623                                         break;\r
624                                 }\r
625                                 \r
626                                 /*\r
627                                  *      Copy substring into buffer.\r
628                                  */\r
629                                 memcpy(rxbuffer,\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
633                                 \r
634                                 /*\r
635                                  *      Copy substring, and add it to\r
636                                  *      the request.\r
637                                  *\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
641                                  */\r
642                                 p = strdup(rxbuffer);\r
643                                 request_data_add(state->request,\r
644                                                  state->request,\r
645                                                  REQUEST_DATA_REGEX | i,\r
646                                                  p, free);\r
647                         }\r
648                         \r
649                 }\r
650                 break;\r
651                 \r
652                 case POLICY_LEX_RX_NOT_EQUALS:\r
653                         regcomp(&reg, this->rhs, REG_EXTENDED|REG_NOSUB);\r
654                         rad_assert(data != NULL);\r
655                         rcode = regexec(&reg, data,\r
656                                         0, NULL, 0);\r
657                         rcode = (rcode != 0);\r
658                         regfree(&reg);\r
659                                 break;\r
660 #endif /* HAVE_REGEX_H */\r
661                 default:\r
662                         rcode = FALSE;\r
663                         break;\r
664                 } /* switch over comparison operators */\r
665                 break;          /* default from first switch over compare */\r
666         }\r
667 \r
668         /*\r
669          *      No trailing &&, ||\r
670          */\r
671         switch (this->child_condition) {\r
672         default:\r
673                 return rcode;\r
674 \r
675         case POLICY_LEX_L_AND:\r
676                 if (!rcode) return rcode; /* FALSE && x == FALSE */\r
677                 break;\r
678 \r
679         case POLICY_LEX_L_OR:\r
680                 if (rcode) return rcode; /* TRUE && x == TRUE */\r
681                 break;\r
682         }\r
683 \r
684         /*\r
685          *      Tail recursion.\r
686          */\r
687         this = (const policy_condition_t *) this->child;\r
688         goto redo;\r
689 \r
690         return 1;               /* should never reach here */\r
691 }\r
692 \r
693 \r
694 /*\r
695  *      Evaluate an 'if' statement\r
696  */\r
697 static int evaluate_if(policy_state_t *state, const policy_item_t *item)\r
698 {\r
699         int rcode;\r
700         const policy_if_t *this;\r
701 \r
702         this = (const policy_if_t *) item;\r
703 \r
704         /*\r
705          *      evaluate_condition calls itself recursively.\r
706          *      We should probably allocate a new state, instead.\r
707          */\r
708         rcode = evaluate_condition(state, this->condition);\r
709         debug_evaluate("IF condition returned %s\n",\r
710                rcode ? "true" : "false");\r
711         if (rcode) {\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
717         }\r
718 \r
719         /*\r
720          *      'if' can fail, if the block it's processing fails.\r
721          */\r
722         return 1;;\r
723 }\r
724 \r
725 \r
726 /*\r
727  *      Make a VALUE_PAIR from a policy_assignment_t*\r
728  *\r
729  *      The assignment operator has to be '='.\r
730  */\r
731 static VALUE_PAIR *assign2vp(REQUEST *request,\r
732                              const policy_assignment_t *assign)\r
733 {\r
734         VALUE_PAIR *vp;\r
735         LRAD_TOKEN operator = T_OP_EQ;\r
736         const char *value = assign->rhs;\r
737         char buffer[2048];\r
738 \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
742                             request, NULL);\r
743                 value = buffer;\r
744         }\r
745 \r
746         /*\r
747          *      This is crappy.. fix it.\r
748          */\r
749         switch (assign->assign) {\r
750         case POLICY_LEX_ASSIGN:\r
751                 operator = T_OP_EQ;\r
752                 break;\r
753 \r
754         case POLICY_LEX_SET_EQUALS:\r
755                 operator = T_OP_SET;\r
756                 break;\r
757         \r
758         case POLICY_LEX_PLUS_EQUALS:\r
759                 operator = T_OP_ADD;\r
760                 break;\r
761         \r
762         default:\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
767                 return NULL;\r
768         }\r
769         \r
770         vp = pairmake(assign->lhs, value, operator);\r
771         if (!vp) {\r
772                 fprintf(stderr, "SHIT: %s %s\n", value, librad_errstr);\r
773         }\r
774 \r
775         return vp;\r
776 }\r
777 \r
778 \r
779 /*\r
780  *      Evaluate a 'packet .= {attrs}' statement\r
781  */\r
782 static int evaluate_attr_list(policy_state_t *state, const policy_item_t *item)\r
783 {\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
788 \r
789         this = (const policy_attributes_t *) item;\r
790 \r
791         switch (this->where) {\r
792         case POLICY_RESERVED_CONTROL:\r
793                 vps = &(state->request->config_items);\r
794                 break;\r
795 \r
796         case POLICY_RESERVED_REQUEST:\r
797                 vps = &(state->request->packet->vps);\r
798                 break;\r
799 \r
800         case POLICY_RESERVED_REPLY:\r
801                 vps = &(state->request->reply->vps);\r
802                 break;\r
803 \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
807                 break;\r
808 \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
812                 break;\r
813 \r
814         default:\r
815                 return 0;\r
816         }\r
817 \r
818         head = NULL;\r
819         tail = &head;\r
820 \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
824                         pairfree(&head);\r
825                         return 0;\r
826                 }\r
827 \r
828                 vp = assign2vp(state->request, (const policy_assignment_t *) attr);\r
829                 if (!vp) {\r
830                         fprintf(stderr, "Failed to allocate VP\n");\r
831                         pairfree(&head);\r
832                         return 0;\r
833                 }\r
834                 *tail = vp;\r
835                 tail = &(vp->next);\r
836         }\r
837 \r
838         switch (this->how) {\r
839         case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */\r
840                 pairfree(vps);\r
841                 *vps = head;\r
842                 break;\r
843 \r
844         case POLICY_LEX_ASSIGN: /* 'union' */\r
845                 pairmove(vps, &head);\r
846                 pairfree(&head);\r
847                 break;\r
848 \r
849         case POLICY_LEX_CONCAT_EQUALS:\r
850                 pairadd(vps, head);\r
851                 break;\r
852 \r
853         default:\r
854                 fprintf(stderr, "HUH?\n");\r
855                 pairfree(&head);\r
856                 return 0;\r
857         }\r
858 \r
859         return 1;\r
860 }\r
861 \r
862 \r
863 /*\r
864  *      Evaluate an 'call foo' statement\r
865  */\r
866 static int evaluate_call(policy_state_t *state, const policy_item_t *item)\r
867 {\r
868         int rcode;\r
869         const policy_call_t *this;\r
870         const rlm_policy_name_t *policy;\r
871 \r
872         this = (const policy_call_t *) item;\r
873 \r
874         policy = rlm_policy_find(state->inst->policies, this->name);\r
875         if (!policy) return 0;  /* not found... */\r
876         \r
877         DEBUG2("rlm_policy: Evaluating policy %s", this->name);\r
878         \r
879         rad_assert(policy->policy->type != POLICY_TYPE_BAD);\r
880         rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);\r
881         \r
882         /*\r
883          *      Push it onto the stack.  Other code will take care of\r
884          *      calling it.\r
885          */\r
886         rcode = policy_stack_push(state, policy->policy);\r
887         if (!rcode) {\r
888                 return rcode;\r
889         }\r
890 \r
891         /*\r
892          *      Function calls always succeed?\r
893          *\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
897          *      we call...\r
898          */\r
899         return 1;\r
900 }\r
901 \r
902 \r
903 /*\r
904  *      State machine stuff.\r
905  */\r
906 typedef int (*policy_evaluate_type_t)(policy_state_t *, const policy_item_t *);\r
907 \r
908 \r
909 /*\r
910  *      MUST be kept in sync with policy_type_t\r
911  */\r
912 static policy_evaluate_type_t evaluate_functions[POLICY_TYPE_NUM_TYPES] = {\r
913         NULL,                   /* POLICY_TYPE_BAD */\r
914         evaluate_if,\r
915         evaluate_condition,\r
916         evaluate_assignment,\r
917         evaluate_attr_list,\r
918         evaluate_print,\r
919         NULL,                   /* define a named policy.. */\r
920         evaluate_call\r
921 };\r
922 \r
923 \r
924 /*\r
925  *      Evaluate a policy, keyed by name.\r
926  */\r
927 static int policy_evaluate_name(policy_state_t *state, const char *name)\r
928 {\r
929         int rcode;\r
930         const policy_item_t *this;\r
931         rlm_policy_name_t mypolicy, *policy;\r
932         \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
936         \r
937         DEBUG2("rlm_policy: Evaluating policy %s", name);\r
938         \r
939         rad_assert(policy->policy->type != POLICY_TYPE_BAD);\r
940         rad_assert(policy->policy->type < POLICY_TYPE_NUM_TYPES);\r
941         \r
942         rcode = policy_stack_push(state, policy->policy);\r
943         if (!rcode) {\r
944                 return RLM_MODULE_FAIL;\r
945         }\r
946 \r
947         /*\r
948          *      FIXME: Look for magic keywords like "return",\r
949          *      where the packet gets accepted/rejected/whatever\r
950          */\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
955                 \r
956                 debug_evaluate("Evaluating at line %d\n",\r
957                                this->lineno);\r
958                 rcode = (*evaluate_functions[this->type])(state,\r
959                                                           this);\r
960                 if (!rcode) {\r
961                         return RLM_MODULE_FAIL;\r
962                 }\r
963         } /* loop until the stack is empty */\r
964 \r
965         return RLM_MODULE_OK;\r
966 }\r
967 \r
968 \r
969 /*\r
970  *      Evaluate, which is pretty close to print, but we look at what\r
971  *      we're printing.\r
972  */\r
973 int rlm_policy_evaluate(rlm_policy_t *inst, REQUEST *request, const char *name)\r
974 {\r
975         int rcode;\r
976         policy_state_t *state;\r
977 \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
982 \r
983         rcode = policy_evaluate_name(state, name);\r
984 \r
985         free(state);\r
986 \r
987         return rcode;           /* evaluated OK. */\r
988 }\r