Added send/recv CoA methods to the server.
[freeradius.git] / src / modules / rlm_policy / parse.c
1 /*
2  * parse.c              Parse a policy language
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2004  Alan DeKok <aland@ox.org>
21  * Copyright 2006  The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include "rlm_policy.h"
28
29 #ifdef HAVE_DIRENT_H
30 #include <dirent.h>
31
32 #ifdef HAVE_SYS_STAT_H
33 #include <sys/stat.h>
34 #endif
35 #endif
36
37 #include <freeradius-devel/modules.h>
38
39 const FR_NAME_NUMBER policy_return_codes[] = {
40         { "reject", RLM_MODULE_REJECT },
41         { "fail", RLM_MODULE_FAIL },
42         { "ok", RLM_MODULE_OK },
43         { "handled", RLM_MODULE_HANDLED },
44         { "invalid", RLM_MODULE_INVALID },
45         { "userlock", RLM_MODULE_USERLOCK },
46         { "notfound", RLM_MODULE_NOTFOUND },
47         { "noop", RLM_MODULE_NOOP },
48         { "updated", RLM_MODULE_UPDATED },
49         { NULL, RLM_MODULE_NUMCODES }
50 };
51
52 /*
53  *      Explanations of what the lexical tokens are.
54  */
55 static const FR_NAME_NUMBER policy_explanations[] = {
56         { "invalid input", POLICY_LEX_BAD },
57         { "end of file", POLICY_LEX_EOF },
58         { "end of line", POLICY_LEX_EOL },
59         { "whitespace", POLICY_LEX_WHITESPACE },
60         { "hash mark", POLICY_LEX_HASH },
61         { "left bracket", POLICY_LEX_L_BRACKET },
62         { "right bracket", POLICY_LEX_R_BRACKET },
63         { "{", POLICY_LEX_LC_BRACKET },
64         { "}", POLICY_LEX_RC_BRACKET },
65         { "comma", POLICY_LEX_COMMA },
66         { "logical AND", POLICY_LEX_L_AND },
67         { "logical OR", POLICY_LEX_L_OR },
68         { "AND", POLICY_LEX_AND },
69         { "OR", POLICY_LEX_OR },
70         { "logical NOT", POLICY_LEX_L_NOT },
71         { "assignment", POLICY_LEX_ASSIGN },
72         { "comparison", POLICY_LEX_CMP_EQUALS },
73         { "comparison", POLICY_LEX_CMP_NOT_EQUALS },
74         { "comparison", POLICY_LEX_LT },
75         { "comparison", POLICY_LEX_GT },
76         { "comparison", POLICY_LEX_LE },
77         { "comparison", POLICY_LEX_GT },
78         { "comparison", POLICY_LEX_RX_EQUALS },
79         { "comparison", POLICY_LEX_RX_NOT_EQUALS },
80         { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
81         { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
82         { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
83         { "bare word", POLICY_LEX_BARE_WORD },
84
85         { NULL, -1 }
86 };
87
88
89 const FR_NAME_NUMBER rlm_policy_tokens[] = {
90         { "EOF", POLICY_LEX_EOF },
91         { "#", POLICY_LEX_HASH },
92         { "(", POLICY_LEX_L_BRACKET },
93         { ")", POLICY_LEX_R_BRACKET },
94         { "{", POLICY_LEX_LC_BRACKET },
95         { "}", POLICY_LEX_RC_BRACKET },
96         { ",", POLICY_LEX_COMMA },
97         { "&&", POLICY_LEX_L_AND },
98         { "||", POLICY_LEX_L_OR },
99         { "&", POLICY_LEX_AND },
100         { "|", POLICY_LEX_OR },
101         { "!", POLICY_LEX_L_NOT },
102         { "=", POLICY_LEX_ASSIGN },
103         { "==", POLICY_LEX_CMP_EQUALS },
104         { "!=", POLICY_LEX_CMP_NOT_EQUALS },
105         { "=*", POLICY_LEX_CMP_TRUE },
106         { "!*", POLICY_LEX_CMP_FALSE },
107         { "<", POLICY_LEX_LT },
108         { ">", POLICY_LEX_GT },
109         { "<=", POLICY_LEX_LE },
110         { ">=", POLICY_LEX_GT },
111         { "=~", POLICY_LEX_RX_EQUALS },
112         { "!~", POLICY_LEX_RX_NOT_EQUALS },
113         { "^=", POLICY_LEX_BEFORE_HEAD_ASSIGN },
114         { "^==", POLICY_LEX_BEFORE_WHERE_ASSIGN },
115         { "^.", POLICY_LEX_BEFORE_HEAD_EQUALS },
116         { "^.=", POLICY_LEX_BEFORE_WHERE_EQUALS },
117         { "$=", POLICY_LEX_AFTER_TAIL_ASSIGN },
118         { "$==", POLICY_LEX_AFTER_WHERE_ASSIGN },
119         { "$.", POLICY_LEX_AFTER_TAIL_EQUALS },
120         { "$.=", POLICY_LEX_AFTER_WHERE_EQUALS },
121         { ".=", POLICY_LEX_CONCAT_EQUALS },
122         { ":=", POLICY_LEX_SET_EQUALS },
123         { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
124         { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
125         { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
126         { "bare word", POLICY_LEX_BARE_WORD },
127
128         { NULL, -1 }
129 };
130
131
132 /*
133  *      Hand-coded lexical analysis of a string.
134  *      Handed input string, updates token, possible a decoded
135  *      string in buffer, and returns the pointer to the next token.
136  *
137  *      Lexical tokens cannot cross a string boundary.
138  */
139 static const char *policy_lex_string(const char *input,
140                                      policy_lex_t *token,
141                                      char *buffer, size_t buflen)
142 {
143         rad_assert(input != NULL);
144         rad_assert(buffer != NULL);
145
146         switch (*input) {
147         case '\0':
148                 *token = POLICY_LEX_EOL;
149                 return NULL;    /* nothing more to do */
150
151         case ' ':
152         case '\t':
153         case '\r':
154         case '\n':
155                 /*
156                  *      Skip over all of the whitespace in one swell foop.
157                  */
158                 *token = POLICY_LEX_WHITESPACE;
159                 while ((*input == ' ') || (*input == '\t') ||
160                        (*input == '\r') || (*input == '\n')) input++;
161                 return input;   /* point to next non-whitespace character */
162
163         case '#':               /* ignore everything to the end of the line */
164                 *token = POLICY_LEX_EOL;
165                 return NULL;
166
167         case '(':
168                 *token = POLICY_LEX_L_BRACKET;
169                 return input + 1;
170
171         case ')':
172                 *token = POLICY_LEX_R_BRACKET;
173                 return input + 1;
174
175         case '{':
176                 *token = POLICY_LEX_LC_BRACKET;
177                 return input + 1;
178
179         case '}':
180                 *token = POLICY_LEX_RC_BRACKET;
181                 return input + 1;
182
183         case ',':
184                 *token = POLICY_LEX_COMMA;
185                 return input + 1;
186
187         case '+':
188                 switch (input[1]) {
189                 case '=':
190                         *token = POLICY_LEX_PLUS_EQUALS;
191                         input++;
192                         break;
193
194                 default:
195                         *token = POLICY_LEX_PLUS;
196                         break;
197                 }
198                 return input + 1;
199
200         case '-':
201                 switch (input[1]) {
202                 case '=':
203                         *token = POLICY_LEX_MINUS_EQUALS;
204                         input++;
205                         break;
206
207                 default:
208                         *token = POLICY_LEX_MINUS;
209                         break;
210                 }
211                 return input + 1;
212
213         case '.':
214                 if (input[1] == '=') {
215                         *token = POLICY_LEX_CONCAT_EQUALS;
216                         return input + 2;
217                 }
218                 *token = POLICY_LEX_BAD;
219                 return input + 1;
220         
221         case '^':
222                 if (input[1] == '.' ) {
223                         if (input[2] == '=') {
224                                 *token = POLICY_LEX_BEFORE_WHERE_EQUALS;
225                                 return input + 3;
226                         }
227                         else {
228                                 *token = POLICY_LEX_BEFORE_HEAD_EQUALS;
229                                 return input + 2;
230                         }
231                 }
232                 else if (input[1] == '=') {
233                         if (input[2] == '=') {
234                                 *token = POLICY_LEX_BEFORE_WHERE_ASSIGN;
235                                 return input + 3;
236                         }
237                         else {
238                                 *token = POLICY_LEX_BEFORE_HEAD_ASSIGN;
239                                 return input + 2;
240                         }
241                 }
242                         
243                 *token = POLICY_LEX_BAD;
244                 return input + 1;
245
246         case '$':
247                 if (input[1] == '.' ) {
248                         if (input[2] == '=') {
249                                 *token = POLICY_LEX_AFTER_WHERE_EQUALS;
250                                 return input + 3;
251                         }
252                         else {
253                                 *token = POLICY_LEX_AFTER_TAIL_EQUALS;
254                                 return input + 2;
255                         }
256                 }
257                 else if (input[1] == '=') {
258                         if (input[2] == '=') {
259                                 *token = POLICY_LEX_AFTER_WHERE_ASSIGN;
260                                 return input + 3;
261                         }
262                         else {
263                                 *token = POLICY_LEX_AFTER_TAIL_ASSIGN;
264                                 return input + 2;
265                         }
266                 }
267
268                 *token = POLICY_LEX_BAD;
269                 return input + 1;
270
271         case ':':
272                 if (input[1] == '=') {
273                         *token = POLICY_LEX_SET_EQUALS;
274                         return input + 2;
275                 }
276                 *token = POLICY_LEX_BAD;
277                 return input + 1;
278
279         case '&':
280                 switch (input[1]) {
281                 case '&':
282                         *token = POLICY_LEX_L_AND;
283                         input++;
284                         break;
285
286                 case '=':
287                         *token = POLICY_LEX_AND_EQUALS;
288                         input++;
289                         break;
290
291                 default:
292                         *token = POLICY_LEX_AND;
293                 }
294                 return input + 1;
295
296         case '|':
297                 switch (input[1]) {
298                 case '|':
299                         *token = POLICY_LEX_L_OR;
300                         input++;
301                         break;
302
303                 case '=':
304                         *token = POLICY_LEX_OR_EQUALS;
305                         input++;
306                         break;
307
308                 default:
309                         *token = POLICY_LEX_OR;
310                 }
311                 return input + 1;
312
313         case '!':
314                 switch (input[1]) {
315                 case '=':
316                         input++;
317                         *token = POLICY_LEX_CMP_NOT_EQUALS;
318                         break;
319
320                 case '~':
321                         input++;
322                         *token = POLICY_LEX_RX_NOT_EQUALS;
323                         break;
324
325                 case '*':
326                         input++;
327                         *token = POLICY_LEX_CMP_FALSE;
328                         break;
329
330                 default:
331                         *token = POLICY_LEX_L_NOT;
332                 }
333                 return input + 1;
334
335         case '=':
336                 switch (input[1]) {
337                 case '=':
338                         input++;
339                         *token = POLICY_LEX_CMP_EQUALS;
340                         break;
341
342                 case '~':
343                         input++;
344                         *token = POLICY_LEX_RX_EQUALS;
345                         break;
346
347                 case '*':
348                         input++;
349                         *token = POLICY_LEX_CMP_TRUE;
350                         break;
351
352                 default:
353                         *token = POLICY_LEX_ASSIGN;
354                 }
355                 return input + 1;
356
357         case '<':
358                 if (input[1] == '=') {
359                         input++;
360                         *token = POLICY_LEX_LE;
361                 } else {
362                         *token = POLICY_LEX_LT;
363                 }
364                 return input + 1;
365
366         case '>':
367                 if (input[1] == '=') {
368                         input++;
369                         *token = POLICY_LEX_GE;
370                 } else {
371                         *token = POLICY_LEX_GT;
372                 }
373                 return input + 1;
374
375         case '"':
376                 if (buflen < 2) {
377                         *token = POLICY_LEX_BAD;
378                         return input + 1;
379                 }
380
381                 input++;
382                 while (*input != '"') {
383                         /*
384                          *      Strings can't pass EOL.
385                          */
386                         if (!*input) {
387                                 return POLICY_LEX_BAD;
388                         }
389
390                         /*
391                          *      FIXME: Embedded quotes?
392                          */
393                         *(buffer++) = *(input++);
394                         buflen--;
395
396                         /*
397                          *      FIXME: Print more warnings?
398                          */
399                         if (buflen == 1) {
400                                 break;
401                         }
402                 }
403                 *buffer = '\0';
404
405                 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
406                 return input + 1; /* skip trailing '"' */
407
408         default:                /* bare word */
409                 break;
410         }
411
412         /*
413          *      It's a bare word, with nowhere to put it.  Die.
414          */
415         if (!buffer) {
416                 *token = POLICY_LEX_BAD;
417                 return input + 1;
418         }
419
420         /*
421          *      Getting one character is stupid.
422          */
423         if (buflen < 2) {
424                 *token = POLICY_LEX_BAD;
425                 return input + 1;
426         }
427
428         /*
429          *      Bare words are [-a-zA-Z0-9.]+
430          */
431         while (*input) {
432                 if (!(((*input >= '0') && (*input <= '9')) ||
433                       ((*input >= 'a') && (*input <= 'z')) ||
434                       ((*input >= 'A') && (*input <= 'Z')) ||
435                       (*input == '-') || (*input == '.') ||
436                       (*input == ':') || (*input == '_'))) {
437                         break;
438                 }
439                 *(buffer++) = *(input++);
440                 buflen--;
441
442                 /*
443                  *      FIXME: Print more warnings?
444                  */
445                 if (buflen == 1) {
446                         break;
447                 }
448         }
449         *buffer = '\0';
450
451         *token = POLICY_LEX_BARE_WORD;
452         return input;
453 }
454
455
456 /*
457  *      We want to lexically analyze a file, so we need a wrapper
458  *      around the lexical analysis of strings.
459  */
460 typedef struct policy_lex_file_t {
461         FILE            *fp;
462         const char      *parse;
463         const char      *filename;
464         int             lineno;
465         int             debug;
466         rbtree_t        *policies;
467         policy_lex_t    token;
468         char            buffer[1024];
469 } policy_lex_file_t;
470
471
472 #define POLICY_LEX_FLAG_RETURN_EOL  (1 << 0)
473 #define POLICY_LEX_FLAG_PEEK        (1 << 1)
474 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
475
476 #define debug_tokens if ((lexer->debug & POLICY_DEBUG_PRINT_TOKENS) && fr_log_fp) fr_printf_log
477
478
479 /*
480  *      Function to return a token saying what it read, and possibly
481  *      a buffer of the quoted string or bare word.
482  */
483 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
484                                     int flags,
485                                     char *mystring, size_t mystringlen)
486 {
487         policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
488
489         if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
490                 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
491         }
492
493         if (!lexer->fp) {
494                 return POLICY_LEX_EOF;
495         }
496
497         /*
498          *      Starting off, the buffer needs to be primed.
499          */
500         if (!lexer->parse) {
501                 lexer->parse = fgets(lexer->buffer,
502                                      sizeof(lexer->buffer),
503                                      lexer->fp);
504
505                 if (!lexer->parse) {
506                         return POLICY_LEX_EOF;
507                 }
508
509                 lexer->lineno = 1;
510         } /* buffer is primed, read stuff */
511
512         if (lexer->token != POLICY_LEX_BAD) {
513                 token = lexer->token;
514                 lexer->token = POLICY_LEX_BAD;
515                 return token;
516         }
517
518         /*
519          *      Ignore whitespace, and keep filling the buffer
520          */
521         while (lexer->parse) {
522                 const char *next;
523
524                 next = policy_lex_string(lexer->parse, &token,
525                                          mystring, mystringlen);
526                 switch (token) {
527                 case POLICY_LEX_WHITESPACE: /* skip whitespace */
528                         lexer->parse = next;
529                         continue;
530
531                 case POLICY_LEX_EOL: /* read another line */
532                         lexer->parse = fgets(lexer->buffer,
533                                              sizeof(lexer->buffer),
534                                              lexer->fp);
535                         lexer->lineno++;
536                         if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
537                                 return POLICY_LEX_EOL;
538                         }
539                         break;  /* read another token */
540
541                 default:        /* return the token */
542                         if (!(flags & POLICY_LEX_FLAG_PEEK)) {
543                                 lexer->parse = next;
544                         }
545                         if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
546                                 debug_tokens("[%s token %s] ",
547                                              (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
548                                              fr_int2str(rlm_policy_tokens,
549                                                           token, "?"));
550                         }
551                         return token;
552                         break;
553                 }
554         } /* loop until EOF */
555
556         /*
557          *      Close it for the user.
558          */
559         fclose(lexer->fp);
560         lexer->fp = NULL;
561
562         return POLICY_LEX_EOF;
563 }
564
565
566 /*
567  *      Push a token back onto the input.
568  *
569  *      FIXME: Push words, too?
570  */
571 static int policy_lex_push_token(policy_lex_file_t *lexer,
572                                  policy_lex_t token)
573 {
574         if (lexer->token != POLICY_LEX_BAD) {
575                 rad_assert(0 == 1);
576                 return 0;
577         }
578
579         lexer->token = token;
580         return 1;
581 }
582
583
584 /*
585  *      Forward declarations.
586  */
587 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
588
589
590 /*
591  *      Map reserved words to tokens, and vice versa.
592  */
593 const FR_NAME_NUMBER policy_reserved_words[] = {
594         { "if", POLICY_RESERVED_IF },
595         { "else", POLICY_RESERVED_ELSE },
596         { "debug", POLICY_RESERVED_DEBUG },
597         { "print", POLICY_RESERVED_PRINT },
598         { "policy", POLICY_RESERVED_POLICY },
599         { "control", POLICY_RESERVED_CONTROL },
600         { "request", POLICY_RESERVED_REQUEST },
601         { "reply", POLICY_RESERVED_REPLY },
602         { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
603         { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
604         { "include", POLICY_RESERVED_INCLUDE },
605         { "return", POLICY_RESERVED_RETURN },
606         { "module", POLICY_RESERVED_MODULE },
607         { NULL, POLICY_RESERVED_UNKNOWN }
608 };
609
610
611 /*
612  *      Simplifies some later coding
613  */
614 static int policy_lex_str2int(policy_lex_file_t *lexer,
615                               const FR_NAME_NUMBER *table, int default_value)
616 {
617         policy_lex_t token;
618         char buffer[256];
619
620         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
621         if (token != POLICY_LEX_BARE_WORD) {
622                 fprintf(stderr, "%s[%d]: Unexpected token\n",
623                         lexer->filename, lexer->lineno);
624                 return default_value;
625         }
626
627         return fr_str2int(table, buffer, default_value);
628 }
629
630
631 /*
632  *      print foo
633  *      print "foo"
634  */
635 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
636 {
637         policy_lex_t token;
638         char mystring[1024];
639         policy_print_t *this;
640
641         debug_tokens("[PRINT] ");
642
643         this = rad_malloc(sizeof(*this));
644         memset(this, 0, sizeof(*this));
645
646         this->item.type = POLICY_TYPE_PRINT;
647         this->item.lineno = lexer->lineno;
648
649         token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
650         if ((token != POLICY_LEX_BARE_WORD) &&
651             (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
652                 fprintf(stderr, "%s[%d]: Bad print command\n",
653                         lexer->filename, lexer->lineno);
654                 rlm_policy_free_item((policy_item_t *) this);
655                 return 0;
656         }
657
658         this->rhs_type = token;
659         this->rhs = strdup(mystring);
660
661         *tail = (policy_item_t *) this;
662
663         return 1;
664 }
665
666
667 /*
668  * (foo == bar), with nested conditionals.
669  */
670 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
671 {
672         int rcode, seen_not = FALSE;
673         policy_lex_t token, compare;
674         char lhs[256], rhs[256];
675         policy_condition_t *this;
676
677         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
678         if (token != POLICY_LEX_L_BRACKET) {
679                 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
680                         lexer->filename, lexer->lineno,
681                         fr_int2str(rlm_policy_tokens, token, lhs));
682                 return 0;
683         }
684
685         this = rad_malloc(sizeof(*this));
686         memset(this, 0, sizeof(*this));
687
688         this->item.type = POLICY_TYPE_CONDITIONAL;
689         this->item.lineno = lexer->lineno;
690
691  redo:
692         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
693         switch (token) {
694         case POLICY_LEX_L_BRACKET:
695                 if (!policy_lex_push_token(lexer, token)) {
696                         rlm_policy_free_item((policy_item_t *) this);
697                         return 0;
698                 }
699
700                 this->compare = POLICY_LEX_L_BRACKET;
701                 this->child_condition = POLICY_LEX_L_BRACKET;
702                 rcode = parse_condition(lexer, &(this->child));
703                 if (!rcode) {
704                         rlm_policy_free_item((policy_item_t *) this);
705                         return rcode;
706                 }
707                 break;
708
709         case POLICY_LEX_L_NOT:
710                 if (seen_not) {
711                         fprintf(stderr, "%s[%d]: Syntax error at \"!!\"\n",
712                                 lexer->filename, lexer->lineno);
713                         rlm_policy_free_item((policy_item_t *) this);
714                         return 0;
715                 }
716
717                 debug_tokens("[NOT] ");
718
719                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
720                 if (token != POLICY_LEX_L_BRACKET) {
721                         seen_not = this->sense = 1;
722                         goto redo;
723                 }
724
725                 this->compare = POLICY_LEX_L_NOT;
726                 rcode = parse_condition(lexer, &(this->child));
727                 if (!rcode) {
728                         rlm_policy_free_item((policy_item_t *) this);
729                         return rcode;
730                 }
731                 break;
732
733         case POLICY_LEX_BARE_WORD:
734                 this->lhs_type = token;
735                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
736                 if (token == POLICY_LEX_L_BRACKET) {
737                         debug_tokens("[IF-CALL %s] ", lhs);
738
739                         /*
740                          *      Function call.
741                          */
742                         if (rlm_policy_find(lexer->policies, lhs) == NULL) {
743                                 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
744                                         lexer->filename, lexer->lineno,
745                                         lhs);
746                                 rlm_policy_free_item((policy_item_t *) this);
747                                 return 0;
748
749                         }
750
751                         /*
752                          *      this->lhs set up below, after "check"
753                          */
754                         this->lhs_type = POLICY_LEX_FUNCTION;
755
756                         /*
757                          *      Copied from parse_call
758                          */
759                         token = policy_lex_file(lexer, 0, NULL, 0);
760                         if (token != POLICY_LEX_L_BRACKET) {
761                                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
762                                         lexer->filename, lexer->lineno,
763                                         fr_int2str(rlm_policy_tokens, token, "?"));
764                                 rlm_policy_free_item((policy_item_t *) this);
765                                 return 0;
766                         }
767
768                         token = policy_lex_file(lexer, 0, NULL, 0);
769                         if (token != POLICY_LEX_R_BRACKET) {
770                                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
771                                         lexer->filename, lexer->lineno,
772                                         fr_int2str(rlm_policy_tokens, token, "?"));
773                                 rlm_policy_free_item((policy_item_t *) this);
774                                 return 0;
775                         }
776                 } /* else it's a comparison? */
777                 goto check;
778
779         case POLICY_LEX_DOUBLE_QUOTED_STRING:
780                 this->lhs_type = token;
781
782                 /*
783                  *      Got word.  May just be test for existence.
784                  */
785         check:
786                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
787                 if (token == POLICY_LEX_R_BRACKET) {
788                         debug_tokens("[TEST %s] ", lhs);
789                         this->lhs = strdup(lhs);
790                         this->compare = POLICY_LEX_CMP_TRUE;
791                         break;
792                 }
793
794                 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
795                 switch (compare) {
796                 case POLICY_LEX_CMP_EQUALS:
797                 case POLICY_LEX_CMP_NOT_EQUALS:
798                 case POLICY_LEX_RX_EQUALS:
799                 case POLICY_LEX_RX_NOT_EQUALS:
800                 case POLICY_LEX_CMP_TRUE:
801                 case POLICY_LEX_CMP_FALSE:
802                 case POLICY_LEX_LT:
803                 case POLICY_LEX_GT:
804                 case POLICY_LEX_LE:
805                 case POLICY_LEX_GE:
806                         break;
807
808                 default:
809                         fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
810                                 lexer->filename, lexer->lineno,
811                                 fr_int2str(rlm_policy_tokens, compare, rhs));
812                         rlm_policy_free_item((policy_item_t *) this);
813                         return 0;
814                 }
815
816                 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
817                 if ((token != POLICY_LEX_BARE_WORD) &&
818                     (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
819                         fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
820                                 lexer->filename, lexer->lineno);
821                         rlm_policy_free_item((policy_item_t *) this);
822                         return 0;
823                 }
824                 debug_tokens("[COMPARE (%s %s %s)] ",
825                        lhs, fr_int2str(rlm_policy_tokens, compare, "?"), rhs);
826                 this->lhs = strdup(lhs);
827                 this->compare = compare;
828                 this->rhs_type = token;
829                 this->rhs = strdup(rhs);
830                 break;
831
832         default:
833                 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
834                         lexer->filename, lexer->lineno);
835                 rlm_policy_free_item((policy_item_t *) this);
836                 return 0;
837         }
838
839         token = policy_lex_file(lexer, 0, NULL, 0);
840         if (token != POLICY_LEX_R_BRACKET) {
841                 fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
842                         lexer->filename, lexer->lineno,
843                         fr_int2str(rlm_policy_tokens, token, "?"));
844                 rlm_policy_free_item((policy_item_t *) this);
845                 return 0;
846         }
847
848         /*
849          *      After the end of condition, we MAY have && or ||
850          */
851         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
852         if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
853                 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
854                 debug_tokens("[%s] ",
855                        fr_int2str(rlm_policy_tokens, token, "?"));
856                 this->child_condition = token;
857                 rcode = parse_condition(lexer, &(this->child));
858                 if (!rcode) {
859                         rlm_policy_free_item((policy_item_t *) this);
860                         return 0;
861                 }
862         }
863
864         *tail = (policy_item_t *) this;
865
866         return 1;
867 }
868
869
870 /*
871  *      if (...) {...}
872  *      if (...) {...} else {...}
873  *      if (...) {...} else if ...
874  */
875 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
876 {
877         int rcode;
878         policy_lex_t token;
879         char mystring[256];
880         policy_if_t *this;
881
882         debug_tokens("[IF] ");
883
884         this = rad_malloc(sizeof(*this));
885         memset(this, 0, sizeof(*this));
886
887         this->item.type = POLICY_TYPE_IF;
888         this->item.lineno = lexer->lineno;
889
890         rcode = parse_condition(lexer, &(this->condition));
891         if (!rcode) {
892                 rlm_policy_free_item((policy_item_t *) this);
893                 return rcode;
894         }
895
896         rcode = parse_block(lexer, &(this->if_true));
897         if (!rcode) {
898                 rlm_policy_free_item((policy_item_t *) this);
899                 return rcode;
900         }
901
902         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
903                                 mystring, sizeof(mystring));
904         if ((token == POLICY_LEX_BARE_WORD) &&
905             (fr_str2int(policy_reserved_words, mystring,
906                           POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
907                 debug_tokens("[ELSE] ");
908                 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
909                 rad_assert(token == POLICY_LEX_BARE_WORD);
910
911                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
912                                         mystring, sizeof(mystring));
913                 if ((token == POLICY_LEX_BARE_WORD) &&
914                     (fr_str2int(policy_reserved_words, mystring,
915                                   POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
916                         token = policy_lex_file(lexer, 0,
917                                                 mystring, sizeof(mystring));
918                         rad_assert(token == POLICY_LEX_BARE_WORD);
919                         rcode = parse_if(lexer, &(this->if_false));
920                 } else {
921                         rcode = parse_block(lexer, &(this->if_false));
922                 }
923                 if (!rcode) {
924                         rlm_policy_free_item((policy_item_t *) this);
925                         return rcode;
926                 }
927         }
928
929         debug_tokens("\n");
930
931         /*
932          *      Empty "if" condition, don't even bother remembering
933          *      it.
934          */
935         if (!this->if_true && !this->if_false) {
936                 debug_tokens("Discarding empty \"if\" statement at line %d\n",
937                              this->item.lineno);
938                 rlm_policy_free_item((policy_item_t *) this);
939                 return 1;
940         }
941
942         *tail = (policy_item_t *) this;
943
944         return 1;
945 }
946
947
948 /*
949  *      Parse a reference to a named policy "foo()"
950  */
951 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
952                       const char *name)
953 {
954         policy_lex_t token;
955         policy_call_t *this;
956
957         debug_tokens("[CALL] ");
958
959         token = policy_lex_file(lexer, 0, NULL, 0);
960         if (token != POLICY_LEX_L_BRACKET) {
961                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
962                         lexer->filename, lexer->lineno,
963                         fr_int2str(rlm_policy_tokens, token, "?"));
964                 return 0;
965         }
966
967         token = policy_lex_file(lexer, 0, NULL, 0);
968         if (token != POLICY_LEX_R_BRACKET) {
969                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
970                         lexer->filename, lexer->lineno,
971                         fr_int2str(rlm_policy_tokens, token, "?"));
972                 return 0;
973         }
974
975         this = rad_malloc(sizeof(*this));
976         memset(this, 0, sizeof(*this));
977
978         this->item.type = POLICY_TYPE_CALL;
979         this->item.lineno = lexer->lineno;
980
981         this->name = strdup(name);
982
983         *tail = (policy_item_t *) this;
984
985         return 1;
986 }
987
988
989 /*
990  *      Edit/update/replace an attribute list
991  */
992 static int parse_attribute_block(policy_lex_file_t *lexer,
993                                  policy_item_t **tail,
994                                  policy_reserved_word_t where)
995 {
996         policy_lex_t token;
997         policy_attributes_t *this;
998         char buffer[32];
999
1000         this = rad_malloc(sizeof(*this));
1001         if (!this) {
1002                 return 0;
1003         }
1004         memset(this, 0, sizeof(*this));
1005         this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
1006         this->item.lineno = lexer->lineno;
1007         this->where = where;
1008
1009         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1010         this->how = token;
1011
1012         switch (token) {
1013         case POLICY_LEX_BEFORE_WHERE_EQUALS:
1014         case POLICY_LEX_AFTER_WHERE_EQUALS:
1015         case POLICY_LEX_BEFORE_WHERE_ASSIGN:
1016         case POLICY_LEX_AFTER_WHERE_ASSIGN:
1017                 if (!parse_condition(lexer, &(this->where_loc))) {
1018                         rlm_policy_free_item((policy_item_t *)this);
1019                         return 0;
1020                 }
1021                 break;
1022         case POLICY_LEX_BEFORE_HEAD_EQUALS:
1023         case POLICY_LEX_AFTER_TAIL_EQUALS:
1024         case POLICY_LEX_BEFORE_HEAD_ASSIGN:
1025         case POLICY_LEX_AFTER_TAIL_ASSIGN:
1026         case POLICY_LEX_ASSIGN:
1027         case POLICY_LEX_SET_EQUALS:
1028         case POLICY_LEX_CONCAT_EQUALS:
1029                 break;
1030
1031         default:
1032                 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
1033                         lexer->filename, lexer->lineno,
1034                         fr_int2str(rlm_policy_tokens, token, "?"));
1035                 return 0;       /* unknown */
1036         }
1037
1038         if (!parse_block(lexer, &(this->attributes))) {
1039                 rlm_policy_free_item((policy_item_t *) this);
1040                 return 0;
1041         }
1042
1043         *tail = (policy_item_t *) this;
1044         return 1;
1045 }
1046
1047
1048 /*
1049  *      Parse a return statement.
1050  */
1051 static int parse_return(policy_lex_file_t *lexer, policy_item_t **tail)
1052 {
1053         int rcode;
1054         policy_lex_t token;
1055         policy_return_t *this;
1056
1057         rcode = policy_lex_str2int(lexer, policy_return_codes,
1058                                    RLM_MODULE_NUMCODES);
1059         if (rcode == RLM_MODULE_NUMCODES) {
1060                 fprintf(stderr, "%s[%d]: Invalid return code\n",
1061                         lexer->filename, lexer->lineno);
1062                 return 0;
1063         }
1064
1065         /*
1066          *      Look for more sutff
1067          */
1068         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
1069                                 NULL, sizeof(0));
1070         if (token != POLICY_LEX_RC_BRACKET) {
1071                 fprintf(stderr, "%s[%d]: return statement must be the last statement in a policy.\n",
1072                         lexer->filename, lexer->lineno);
1073                 return 0;
1074         }
1075
1076         this = rad_malloc(sizeof(*this));
1077         memset(this, 0, sizeof(*this));
1078
1079         this->item.type = POLICY_TYPE_RETURN;
1080         this->item.lineno = lexer->lineno;
1081         this->rcode = rcode;
1082
1083         *tail = (policy_item_t *) this;
1084
1085         return 1;
1086 }
1087
1088
1089 const FR_NAME_NUMBER policy_component_names[] = {
1090         { "authenticate", RLM_COMPONENT_AUTH },
1091         { "authorize", RLM_COMPONENT_AUTZ },
1092         { "preacct", RLM_COMPONENT_PREACCT },
1093         { "accounting", RLM_COMPONENT_ACCT },
1094         { "session", RLM_COMPONENT_SESS },
1095         { "pre-proxy", RLM_COMPONENT_PRE_PROXY },
1096         { "post-proxy", RLM_COMPONENT_POST_PROXY },
1097         { "post-auth", RLM_COMPONENT_POST_AUTH },
1098 #ifdef WITH_COA
1099         { "recv-coa", RLM_COMPONENT_RECV_COA },
1100         { "send-coa", RLM_COMPONENT_SEND_COA },
1101 #endif
1102         { NULL, RLM_COMPONENT_COUNT }
1103 };
1104
1105 /*
1106  *      Parse a module statement.
1107  */
1108 static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
1109 {
1110         int component;
1111         policy_lex_t token;
1112         policy_module_t *this;
1113         char *p;
1114         const char *section_name;
1115         char filename[1024];
1116         char buffer[2048];
1117         CONF_SECTION *cs, *subcs;
1118         modcallable *mc;
1119
1120         /*
1121          *      And the filename
1122          */
1123         token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1124         if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1125                 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1126                         lexer->filename, lexer->lineno,
1127                         fr_int2str(rlm_policy_tokens, token, "?"));
1128                 return 0;
1129         }
1130
1131         /*
1132          *      See if we're including all of the files in a subdirectory.
1133          */
1134         strlcpy(buffer, lexer->filename, sizeof(buffer));
1135         p = strrchr(buffer, '/');
1136         if (p) {
1137                 strlcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1138         } else {
1139                 snprintf(buffer, sizeof(buffer), "%s/%s",
1140                          radius_dir, filename);
1141         }
1142
1143         /*
1144          *      Include section calling a module.
1145          */
1146         debug_tokens("including module section from file %s\n", buffer);
1147         cs = cf_file_read(buffer);
1148         if (!cs) {
1149                 return 0;       /* it prints out error messages */
1150         }
1151
1152         /*
1153          *      The outer section is called "main", and can be ignored.
1154          *      It should be a section, so there should be a subsection.
1155          */
1156         subcs = cf_subsection_find_next(cs, NULL, NULL);
1157         if (!subcs) {
1158                 fprintf(stderr, "%s[%d]: Expected section containing modules\n",
1159                         lexer->filename, lexer->lineno);
1160                 cf_section_free(&cs);
1161                 return 0;
1162         }
1163
1164         section_name = cf_section_name1(subcs);
1165         rad_assert(section_name != NULL);
1166         component = fr_str2int(policy_component_names, section_name,
1167                                  RLM_COMPONENT_COUNT);
1168         if (component == RLM_COMPONENT_COUNT) {
1169                 fprintf(stderr, "%s[%d]: Invalid section name \"%s\"\n",
1170                         lexer->filename, lexer->lineno, section_name);
1171                 cf_section_free(&cs);
1172                 return 0;
1173         }
1174
1175         /*
1176          *      Compile the module entry.
1177          */
1178         mc = compile_modgroup(NULL, component, subcs);
1179         if (!mc) {
1180                 cf_section_free(&cs);
1181                 return 0;       /* more often results in calling exit... */
1182         }
1183
1184         this = rad_malloc(sizeof(*this));
1185         memset(this, 0, sizeof(*this));
1186
1187         this->item.type = POLICY_TYPE_MODULE;
1188         this->item.lineno = lexer->lineno;
1189         this->component = component;
1190         this->cs = cs;
1191         this->mc = mc;
1192
1193         *tail = (policy_item_t *) this;
1194
1195         return 1;
1196 }
1197
1198
1199 /*
1200  *      Parse one statement.  'foo = bar', or 'if (...) {...}', or '{...}',
1201  *      and so on.
1202  */
1203 static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
1204 {
1205         int rcode;
1206         policy_reserved_word_t reserved;
1207         policy_lex_t token, assign;
1208         char lhs[256], rhs[256];
1209         policy_assignment_t *this;
1210
1211         /*
1212          *      See what kind of token we have.
1213          */
1214         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
1215         switch (token) {
1216         case POLICY_LEX_LC_BRACKET:
1217                 rcode = parse_block(lexer, tail);
1218                 if (!rcode) {
1219                         return 0;
1220                 }
1221                 break;
1222
1223         case POLICY_LEX_BARE_WORD:
1224                 reserved = fr_str2int(policy_reserved_words,
1225                                         lhs,
1226                                         POLICY_RESERVED_UNKNOWN);
1227                 switch (reserved) {
1228                 case POLICY_RESERVED_IF:
1229                         if (parse_if(lexer, tail)) {
1230                                 return 1;
1231                         }
1232                         return 0;
1233                         break;
1234
1235                 case POLICY_RESERVED_CONTROL:
1236                 case POLICY_RESERVED_REQUEST:
1237                 case POLICY_RESERVED_REPLY:
1238                 case POLICY_RESERVED_PROXY_REQUEST:
1239                 case POLICY_RESERVED_PROXY_REPLY:
1240                         if (parse_attribute_block(lexer, tail,
1241                                                   reserved))
1242                                 return 1;
1243                         return 0;
1244                         break;
1245
1246                 case POLICY_RESERVED_PRINT:
1247                         if (parse_print(lexer, tail)) {
1248                                 return 1;
1249                         }
1250                         return 0;
1251                         break;
1252
1253                 case POLICY_RESERVED_RETURN:
1254                         if (parse_return(lexer, tail)) {
1255                                 return 1;
1256                         }
1257                         return 0;
1258                         break;
1259
1260                 case POLICY_RESERVED_MODULE:
1261                         if (parse_module(lexer, tail)) {
1262                                 return 1;
1263                         }
1264                         return 0;
1265                         break;
1266
1267                 case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
1268                         /*
1269                          *      Is a named policy, parse the reference to it.
1270                          */
1271                         if (rlm_policy_find(lexer->policies, lhs) != NULL) {
1272                                 if (!parse_call(lexer, tail, lhs)) {
1273                                         return 0;
1274                                 }
1275                                 return 1;
1276                         }
1277
1278                         {
1279                                 const DICT_ATTR *dattr;
1280
1281                                 /*
1282                                  *      Bare words MUST be dictionary attributes
1283                                  */
1284
1285                                 dattr = dict_attrbyname(lhs);
1286                                 if (!dattr) {
1287                                         fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
1288                                                 lexer->filename, lexer->lineno, lhs);
1289                                         return 0;
1290                                 }
1291                                 debug_tokens("%s[%d]: Got attribute %s\n",
1292                                              lexer->filename, lexer->lineno,
1293                                              lhs);
1294                         }
1295                         break;
1296
1297                 default:
1298                         fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
1299                                 lexer->filename, lexer->lineno, lhs);
1300                         return 0;
1301                 } /* switch over reserved words */
1302                 break;
1303
1304                 /*
1305                  *      Return from nested blocks.
1306                  */
1307         case POLICY_LEX_RC_BRACKET:
1308                 policy_lex_push_token(lexer, token);
1309                 return 2;       /* magic */
1310
1311         case POLICY_LEX_EOF:    /* nothing more to do */
1312                 return 3;
1313
1314         default:
1315                 fprintf(stderr, "%s[%d]: Unexpected %s\n",
1316                         lexer->filename, lexer->lineno,
1317                         fr_int2str(policy_explanations,
1318                                      token, "string"));
1319                 break;
1320         }
1321
1322         /*
1323          *      Parse a bare statement.
1324          */
1325         assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1326         switch (assign) {
1327         case POLICY_LEX_ASSIGN:
1328         case POLICY_LEX_SET_EQUALS:
1329         case POLICY_LEX_AND_EQUALS:
1330         case POLICY_LEX_OR_EQUALS:
1331         case POLICY_LEX_PLUS_EQUALS:
1332                 break;
1333
1334         default:
1335                 fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
1336                         lexer->filename, lexer->lineno,
1337                         fr_int2str(policy_explanations,
1338                                      assign, "string"));
1339                 return 0;
1340         }
1341
1342         this = rad_malloc(sizeof(*this));
1343         memset(this, 0, sizeof(*this));
1344
1345         this->item.type = POLICY_TYPE_ASSIGNMENT;
1346         this->item.lineno = lexer->lineno;
1347
1348         token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1349         if ((token != POLICY_LEX_BARE_WORD) &&
1350             (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
1351                 fprintf(stderr, "%s[%d]: Unexpected rhs %s\n",
1352                         lexer->filename, lexer->lineno,
1353                         fr_int2str(policy_explanations,
1354                                      token, "string"));
1355                 rlm_policy_free_item((policy_item_t *) this);
1356                 return 0;
1357         }
1358         this->rhs_type = token;
1359         this->rhs = strdup(rhs);
1360
1361         token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1362                                 rhs, sizeof(rhs));
1363         if (token != POLICY_LEX_EOL) {
1364                 fprintf(stderr, "%s[%d]: Expected EOL\n",
1365                         lexer->filename, lexer->lineno);
1366                 rlm_policy_free_item((policy_item_t *) this);
1367                 return 0;
1368         }
1369         debug_tokens("[ASSIGN %s %s %s]\n",
1370                lhs, fr_int2str(rlm_policy_tokens, assign, "?"), rhs);
1371
1372         /*
1373          *      Fill in the assignment struct
1374          */
1375         this->lhs = strdup(lhs);
1376         this->assign = assign;
1377
1378         *tail = (policy_item_t *) this;
1379
1380         return 1;
1381 }
1382
1383
1384 /*
1385  *      Parse block of statements.  The block has already been checked
1386  *      to begin with a '{'.
1387  */
1388 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail)
1389 {
1390         int rcode;
1391         policy_lex_t token;
1392
1393         debug_tokens("[BLOCK] ");
1394
1395         token = policy_lex_file(lexer, 0, NULL, 0);
1396         if (token != POLICY_LEX_LC_BRACKET) {
1397                 fprintf(stderr, "%s[%d]: Expected '{'\n",
1398                         lexer->filename, lexer->lineno);
1399                 return 0;
1400         }
1401
1402         while ((rcode = parse_statement(lexer, tail)) != 0) {
1403                 if (rcode == 2) {
1404                         token = policy_lex_file(lexer, 0, NULL, 0);
1405                         if (token != POLICY_LEX_RC_BRACKET) {
1406                                 fprintf(stderr, "%s[%d]: Expected '}'\n",
1407                                         lexer->filename, lexer->lineno);
1408                                 return 0;
1409                         }
1410                         return 1;
1411                 }
1412                 rad_assert(*tail != NULL);
1413                 /* parse_statement must fill this in */
1414                 while (*tail) tail = &((*tail)->next);
1415         }
1416         debug_tokens("\n");
1417
1418         /*
1419          *      Parse statement failed.
1420          */
1421         return 0;
1422 }
1423
1424
1425 /*
1426  *      Parse debugging statements
1427  */
1428 static int parse_debug(policy_lex_file_t *lexer)
1429 {
1430         int rcode = 0;
1431         policy_lex_t token;
1432         char buffer[32];
1433
1434         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1435         if (token != POLICY_LEX_BARE_WORD) {
1436                 fprintf(stderr, "%s[%d]: Bad debug command\n",
1437                         lexer->filename, lexer->lineno);
1438                 return 0;
1439         }
1440
1441         if (strcasecmp(buffer, "none") == 0) {
1442                 lexer->debug = POLICY_DEBUG_NONE;
1443                 rcode = 1;
1444
1445         } else if (strcasecmp(buffer, "peek") == 0) {
1446                 lexer->debug |= POLICY_DEBUG_PEEK;
1447                 rcode = 1;
1448
1449         } else if (strcasecmp(buffer, "print_tokens") == 0) {
1450                 lexer->debug |= POLICY_DEBUG_PRINT_TOKENS;
1451                 rcode = 1;
1452
1453         } else if (strcasecmp(buffer, "print_policy") == 0) {
1454                 lexer->debug |= POLICY_DEBUG_PRINT_POLICY;
1455                 rcode = 1;
1456
1457         } else if (strcasecmp(buffer, "evaluate") == 0) {
1458                 lexer->debug |= POLICY_DEBUG_EVALUATE;
1459                 rcode = 1;
1460         }
1461
1462         if (rcode) {
1463                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1464                                         NULL, 0);
1465                 if (token != POLICY_LEX_EOL) {
1466                         fprintf(stderr, "%s[%d]: Expected EOL\n",
1467                                 lexer->filename, lexer->lineno);
1468                         return 0;
1469                 }
1470         } else {
1471                 fprintf(stderr, "%s[%d]: Bad debug command \"%s\"\n",
1472                         lexer->filename, lexer->lineno, buffer);
1473                 return 0;
1474         }
1475
1476         return 1;
1477 }
1478
1479
1480 /*
1481  *      Parse a named policy "policy foo {...}"
1482  */
1483 static int parse_named_policy(policy_lex_file_t *lexer)
1484 {
1485         int rcode;
1486         policy_lex_t token;
1487         char mystring[256];
1488         policy_named_t *this;
1489         DICT_ATTR *dattr;
1490
1491         debug_tokens("[POLICY] ");
1492
1493         this = rad_malloc(sizeof(*this));
1494         memset(this, 0, sizeof(*this));
1495
1496         this->item.type = POLICY_TYPE_NAMED_POLICY;
1497         this->item.lineno = lexer->lineno;
1498
1499         token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
1500         if (token != POLICY_LEX_BARE_WORD) {
1501                 fprintf(stderr, "%s[%d]: Expected policy name, got \"%s\"\n",
1502                         lexer->filename, lexer->lineno,
1503                         fr_int2str(rlm_policy_tokens, token, "?"));
1504                 rlm_policy_free_item((policy_item_t *) this);
1505                 return 0;
1506         }
1507
1508         dattr = dict_attrbyname(mystring);
1509         if (dattr) {
1510                 fprintf(stderr, "%s[%d]: Invalid policy name \"%s\": it is already defined as a dictionary attribute\n",
1511                         lexer->filename, lexer->lineno, mystring);
1512                 rlm_policy_free_item((policy_item_t *) this);
1513                 return 0;
1514         }
1515
1516         this->name = strdup(mystring);
1517         rcode = parse_block(lexer, &(this->policy));
1518         if (!rcode) {
1519                 rlm_policy_free_item((policy_item_t *) this);
1520                 return rcode;
1521         }
1522
1523         /*
1524          *      And insert it into the tree of policies.
1525          *
1526          *      For now, policy names aren't scoped, they're global.
1527          */
1528         if (!rlm_policy_insert(lexer->policies, this)) {
1529                 radlog(L_ERR, "Failed to insert policy \"%s\"", this->name);
1530                 rlm_policy_free_item((policy_item_t *) this);
1531                 return 0;
1532         }
1533
1534         if ((lexer->debug & POLICY_DEBUG_PRINT_POLICY) != 0) {
1535                 rlm_policy_print(this);
1536         }
1537
1538         return 1;
1539 }
1540
1541
1542 /*
1543  *      Parse an "include filename" statement
1544  *
1545  *      FIXME: Tie this file into the CONF_SECTION for HUP handling!
1546  */
1547 static int parse_include(policy_lex_file_t *lexer)
1548 {
1549         char *p;
1550         policy_lex_t token;
1551         char filename[1024];
1552         char buffer[2048];
1553
1554         token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1555         if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1556                 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1557                         lexer->filename, lexer->lineno,
1558                         fr_int2str(rlm_policy_tokens, token, "?"));
1559                 return 0;
1560         }
1561
1562         /*
1563          *      See if we're including all of the files in a subdirectory.
1564          */
1565         strlcpy(buffer, lexer->filename, sizeof(buffer));
1566         p = strrchr(buffer, '/');
1567         if (p) {
1568                 strlcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1569
1570 #ifdef HAVE_DIRENT_H
1571                 p = strrchr(p + 1, '/');
1572                 if (p && !p[1]) {
1573                         DIR             *dir;
1574                         struct dirent   *dp;
1575
1576                         p++;
1577
1578                         dir = opendir(buffer);
1579                         if (!dir) {
1580                                 fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
1581                                         lexer->filename, lexer->lineno,
1582                                         buffer, strerror(errno));
1583                                 return 0;
1584                         }
1585
1586                         /*
1587                          *      Read the directory, ignoring "." files.
1588                          */
1589                         while ((dp = readdir(dir)) != NULL) {
1590                                 struct stat buf;
1591
1592                                 if (dp->d_name[0] == '.') continue;
1593                                 if (strchr(dp->d_name, '~') != NULL) continue;
1594
1595                                 strlcpy(p, dp->d_name,
1596                                         sizeof(buffer) - (p - buffer));
1597
1598                                 if ((stat(buffer, &buf) != 0) ||
1599                                     S_ISDIR(buf.st_mode)) continue;
1600
1601                                 debug_tokens("\nincluding file %s\n", buffer);
1602                                 if (!rlm_policy_parse(lexer->policies, buffer)) {
1603                                         closedir(dir);
1604                                         return 0;
1605                                 }
1606                         }
1607                         closedir(dir);
1608                         return 1;
1609                 } /* else it must have been a normalx file */
1610 #endif
1611         } else {
1612                 snprintf(buffer, sizeof(buffer), "%s/%s",
1613                          radius_dir, filename);
1614         }
1615
1616         /*
1617          *      Handle one include file.
1618          */
1619         debug_tokens("\nincluding file %s\n", buffer);
1620         if (!rlm_policy_parse(lexer->policies, buffer)) {
1621                 return 0;
1622         }
1623
1624         return 1;
1625 }
1626
1627
1628 /*
1629  *      Parse data from a file into a policy language.
1630  */
1631 int rlm_policy_parse(rbtree_t *policies, const char *filename)
1632 {
1633         FILE *fp;
1634         policy_lex_t token;
1635         policy_lex_file_t mylexer, *lexer = NULL;
1636         char buffer[32];
1637
1638         fp = fopen(filename, "r");
1639         if (!fp) {
1640                 fprintf(stderr, "Failed to open %s: %s\n",
1641                         filename, strerror(errno));
1642                 return 0;
1643         }
1644
1645         lexer = &mylexer;
1646         memset(lexer, 0, sizeof(*lexer));
1647         lexer->filename = filename;
1648         lexer->fp = fp;
1649         lexer->token = POLICY_LEX_BAD;
1650         lexer->parse = NULL;    /* initial input */
1651         lexer->policies = policies;
1652
1653         do {
1654                 int reserved;
1655
1656                 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1657                 switch (token) {
1658                 case POLICY_LEX_BARE_WORD:
1659                         reserved = fr_str2int(policy_reserved_words,
1660                                                 buffer,
1661                                                 POLICY_RESERVED_UNKNOWN);
1662                         switch (reserved) {
1663                         case POLICY_RESERVED_POLICY:
1664                                 if (!parse_named_policy(lexer)) {
1665                                         return 0;
1666                                 }
1667                                 break;
1668
1669                         case POLICY_RESERVED_INCLUDE:
1670                                 if (!parse_include(lexer)) {
1671                                         return 0;
1672                                 }
1673                                 break;
1674
1675                         case POLICY_RESERVED_DEBUG:
1676                                 if (!parse_debug(lexer)) {
1677                                         return 0;
1678                                 }
1679                                 break;
1680
1681                         default:
1682                                 fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
1683                                         lexer->filename, lexer->lineno,
1684                                         buffer);
1685                                 return 0;
1686                                 break;
1687                         } /* switch over reserved words */
1688
1689                 case POLICY_LEX_EOF:
1690                         break;
1691
1692                 default:
1693                         fprintf(stderr, "%s[%d]: Illegal input\n",
1694                                 lexer->filename, lexer->lineno);
1695                         return 0;
1696                 }
1697         } while (token != POLICY_LEX_EOF);
1698
1699         if (((lexer->debug & POLICY_DEBUG_PRINT_POLICY) != 0) && fr_log_fp) {
1700                 fprintf(fr_log_fp, "# rlm_policy \n");
1701         }
1702
1703         debug_tokens("--------------------------------------------------\n");
1704
1705         return 1;
1706 }
1707