Merge branch 'sam'
[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
145         switch (*input) {
146         case '\0':
147                 *token = POLICY_LEX_EOL;
148                 return NULL;    /* nothing more to do */
149
150         case ' ':
151         case '\t':
152         case '\r':
153         case '\n':
154                 /*
155                  *      Skip over all of the whitespace in one swell foop.
156                  */
157                 *token = POLICY_LEX_WHITESPACE;
158                 while ((*input == ' ') || (*input == '\t') ||
159                        (*input == '\r') || (*input == '\n')) input++;
160                 return input;   /* point to next non-whitespace character */
161
162         case '#':               /* ignore everything to the end of the line */
163                 *token = POLICY_LEX_EOL;
164                 return NULL;
165
166         case '(':
167                 *token = POLICY_LEX_L_BRACKET;
168                 return input + 1;
169
170         case ')':
171                 *token = POLICY_LEX_R_BRACKET;
172                 return input + 1;
173
174         case '{':
175                 *token = POLICY_LEX_LC_BRACKET;
176                 return input + 1;
177
178         case '}':
179                 *token = POLICY_LEX_RC_BRACKET;
180                 return input + 1;
181
182         case ',':
183                 *token = POLICY_LEX_COMMA;
184                 return input + 1;
185
186         case '+':
187                 switch (input[1]) {
188                 case '=':
189                         *token = POLICY_LEX_PLUS_EQUALS;
190                         input++;
191                         break;
192
193                 default:
194                         *token = POLICY_LEX_PLUS;
195                         break;
196                 }
197                 return input + 1;
198
199         case '-':
200                 switch (input[1]) {
201                 case '=':
202                         *token = POLICY_LEX_MINUS_EQUALS;
203                         input++;
204                         break;
205
206                 default:
207                         *token = POLICY_LEX_MINUS;
208                         break;
209                 }
210                 return input + 1;
211
212         case '.':
213                 if (input[1] == '=') {
214                         *token = POLICY_LEX_CONCAT_EQUALS;
215                         return input + 2;
216                 }
217                 *token = POLICY_LEX_BAD;
218                 return input + 1;
219         
220         case '^':
221                 if (input[1] == '.' ) {
222                         if (input[2] == '=') {
223                                 *token = POLICY_LEX_BEFORE_WHERE_EQUALS;
224                                 return input + 3;
225                         }
226                         else {
227                                 *token = POLICY_LEX_BEFORE_HEAD_EQUALS;
228                                 return input + 2;
229                         }
230                 }
231                 else if (input[1] == '=') {
232                         if (input[2] == '=') {
233                                 *token = POLICY_LEX_BEFORE_WHERE_ASSIGN;
234                                 return input + 3;
235                         }
236                         else {
237                                 *token = POLICY_LEX_BEFORE_HEAD_ASSIGN;
238                                 return input + 2;
239                         }
240                 }
241                         
242                 *token = POLICY_LEX_BAD;
243                 return input + 1;
244
245         case '$':
246                 if (input[1] == '.' ) {
247                         if (input[2] == '=') {
248                                 *token = POLICY_LEX_AFTER_WHERE_EQUALS;
249                                 return input + 3;
250                         }
251                         else {
252                                 *token = POLICY_LEX_AFTER_TAIL_EQUALS;
253                                 return input + 2;
254                         }
255                 }
256                 else if (input[1] == '=') {
257                         if (input[2] == '=') {
258                                 *token = POLICY_LEX_AFTER_WHERE_ASSIGN;
259                                 return input + 3;
260                         }
261                         else {
262                                 *token = POLICY_LEX_AFTER_TAIL_ASSIGN;
263                                 return input + 2;
264                         }
265                 }
266
267                 *token = POLICY_LEX_BAD;
268                 return input + 1;
269
270         case ':':
271                 if (input[1] == '=') {
272                         *token = POLICY_LEX_SET_EQUALS;
273                         return input + 2;
274                 }
275                 *token = POLICY_LEX_BAD;
276                 return input + 1;
277
278         case '&':
279                 switch (input[1]) {
280                 case '&':
281                         *token = POLICY_LEX_L_AND;
282                         input++;
283                         break;
284
285                 case '=':
286                         *token = POLICY_LEX_AND_EQUALS;
287                         input++;
288                         break;
289
290                 default:
291                         *token = POLICY_LEX_AND;
292                 }
293                 return input + 1;
294
295         case '|':
296                 switch (input[1]) {
297                 case '|':
298                         *token = POLICY_LEX_L_OR;
299                         input++;
300                         break;
301
302                 case '=':
303                         *token = POLICY_LEX_OR_EQUALS;
304                         input++;
305                         break;
306
307                 default:
308                         *token = POLICY_LEX_OR;
309                 }
310                 return input + 1;
311
312         case '!':
313                 switch (input[1]) {
314                 case '=':
315                         input++;
316                         *token = POLICY_LEX_CMP_NOT_EQUALS;
317                         break;
318
319                 case '~':
320                         input++;
321                         *token = POLICY_LEX_RX_NOT_EQUALS;
322                         break;
323
324                 case '*':
325                         input++;
326                         *token = POLICY_LEX_CMP_FALSE;
327                         break;
328
329                 default:
330                         *token = POLICY_LEX_L_NOT;
331                 }
332                 return input + 1;
333
334         case '=':
335                 switch (input[1]) {
336                 case '=':
337                         input++;
338                         *token = POLICY_LEX_CMP_EQUALS;
339                         break;
340
341                 case '~':
342                         input++;
343                         *token = POLICY_LEX_RX_EQUALS;
344                         break;
345
346                 case '*':
347                         input++;
348                         *token = POLICY_LEX_CMP_TRUE;
349                         break;
350
351                 default:
352                         *token = POLICY_LEX_ASSIGN;
353                 }
354                 return input + 1;
355
356         case '<':
357                 if (input[1] == '=') {
358                         input++;
359                         *token = POLICY_LEX_LE;
360                 } else {
361                         *token = POLICY_LEX_LT;
362                 }
363                 return input + 1;
364
365         case '>':
366                 if (input[1] == '=') {
367                         input++;
368                         *token = POLICY_LEX_GE;
369                 } else {
370                         *token = POLICY_LEX_GT;
371                 }
372                 return input + 1;
373
374         case '"':
375                 if (!buffer || (buflen < 2)) {
376                         *token = POLICY_LEX_BAD;
377                         return input + 1;
378                 }
379
380                 input++;
381                 while (*input != '"') {
382                         /*
383                          *      Strings can't pass EOL.
384                          */
385                         if (!*input) {
386                                 return POLICY_LEX_BAD;
387                         }
388
389                         /*
390                          *      FIXME: Embedded quotes?
391                          */
392                         *(buffer++) = *(input++);
393                         buflen--;
394
395                         /*
396                          *      FIXME: Print more warnings?
397                          */
398                         if (buflen == 1) {
399                                 break;
400                         }
401                 }
402                 *buffer = '\0';
403
404                 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
405                 return input + 1; /* skip trailing '"' */
406
407         default:                /* bare word */
408                 break;
409         }
410
411         /*
412          *      It's a bare word, with nowhere to put it.  Die.
413          */
414         if (!buffer) {
415                 *token = POLICY_LEX_BAD;
416                 return input + 1;
417         }
418
419         /*
420          *      Getting one character is stupid.
421          */
422         if (buflen < 2) {
423                 *token = POLICY_LEX_BAD;
424                 return input + 1;
425         }
426
427         /*
428          *      Bare words are [-a-zA-Z0-9.]+
429          */
430         while (*input) {
431                 if (!(((*input >= '0') && (*input <= '9')) ||
432                       ((*input >= 'a') && (*input <= 'z')) ||
433                       ((*input >= 'A') && (*input <= 'Z')) ||
434                       (*input == '-') || (*input == '.') ||
435                       (*input == ':') || (*input == '_'))) {
436                         break;
437                 }
438                 *(buffer++) = *(input++);
439                 buflen--;
440
441                 /*
442                  *      FIXME: Print more warnings?
443                  */
444                 if (buflen == 1) {
445                         break;
446                 }
447         }
448         *buffer = '\0';
449
450         *token = POLICY_LEX_BARE_WORD;
451         return input;
452 }
453
454
455 /*
456  *      We want to lexically analyze a file, so we need a wrapper
457  *      around the lexical analysis of strings.
458  */
459 typedef struct policy_lex_file_t {
460         FILE            *fp;
461         const char      *parse;
462         const char      *filename;
463         int             lineno;
464         int             debug;
465         rbtree_t        *policies;
466         policy_lex_t    token;
467         char            buffer[1024];
468 } policy_lex_file_t;
469
470
471 #define POLICY_LEX_FLAG_RETURN_EOL  (1 << 0)
472 #define POLICY_LEX_FLAG_PEEK        (1 << 1)
473 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
474
475 #define debug_tokens if ((lexer->debug & POLICY_DEBUG_PRINT_TOKENS) && fr_log_fp) fr_printf_log
476
477
478 /*
479  *      Function to return a token saying what it read, and possibly
480  *      a buffer of the quoted string or bare word.
481  */
482 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
483                                     int flags,
484                                     char *mystring, size_t mystringlen)
485 {
486         policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
487
488         if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
489                 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
490         }
491
492         if (!lexer->fp) {
493                 return POLICY_LEX_EOF;
494         }
495
496         /*
497          *      Starting off, the buffer needs to be primed.
498          */
499         if (!lexer->parse) {
500                 lexer->parse = fgets(lexer->buffer,
501                                      sizeof(lexer->buffer),
502                                      lexer->fp);
503
504                 if (!lexer->parse) {
505                         return POLICY_LEX_EOF;
506                 }
507
508                 lexer->lineno = 1;
509         } /* buffer is primed, read stuff */
510
511         if (lexer->token != POLICY_LEX_BAD) {
512                 token = lexer->token;
513                 lexer->token = POLICY_LEX_BAD;
514                 return token;
515         }
516
517         /*
518          *      Ignore whitespace, and keep filling the buffer
519          */
520         while (lexer->parse) {
521                 const char *next;
522
523                 next = policy_lex_string(lexer->parse, &token,
524                                          mystring, mystringlen);
525                 switch (token) {
526                 case POLICY_LEX_WHITESPACE: /* skip whitespace */
527                         lexer->parse = next;
528                         continue;
529
530                 case POLICY_LEX_EOL: /* read another line */
531                         lexer->parse = fgets(lexer->buffer,
532                                              sizeof(lexer->buffer),
533                                              lexer->fp);
534                         lexer->lineno++;
535                         if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
536                                 return POLICY_LEX_EOL;
537                         }
538                         break;  /* read another token */
539
540                 default:        /* return the token */
541                         if (!(flags & POLICY_LEX_FLAG_PEEK)) {
542                                 lexer->parse = next;
543                         }
544                         if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
545                                 debug_tokens("[%s token %s] ",
546                                              (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
547                                              fr_int2str(rlm_policy_tokens,
548                                                           token, "?"));
549                         }
550                         return token;
551                         break;
552                 }
553         } /* loop until EOF */
554
555         /*
556          *      Close it for the user.
557          */
558         fclose(lexer->fp);
559         lexer->fp = NULL;
560
561         return POLICY_LEX_EOF;
562 }
563
564
565 /*
566  *      Push a token back onto the input.
567  *
568  *      FIXME: Push words, too?
569  */
570 static int policy_lex_push_token(policy_lex_file_t *lexer,
571                                  policy_lex_t token)
572 {
573         if (lexer->token != POLICY_LEX_BAD) {
574                 rad_assert(0 == 1);
575                 return 0;
576         }
577
578         lexer->token = token;
579         return 1;
580 }
581
582
583 /*
584  *      Forward declarations.
585  */
586 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
587
588
589 /*
590  *      Map reserved words to tokens, and vice versa.
591  */
592 const FR_NAME_NUMBER policy_reserved_words[] = {
593         { "if", POLICY_RESERVED_IF },
594         { "else", POLICY_RESERVED_ELSE },
595         { "debug", POLICY_RESERVED_DEBUG },
596         { "print", POLICY_RESERVED_PRINT },
597         { "policy", POLICY_RESERVED_POLICY },
598         { "control", POLICY_RESERVED_CONTROL },
599         { "request", POLICY_RESERVED_REQUEST },
600         { "reply", POLICY_RESERVED_REPLY },
601         { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
602         { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
603         { "include", POLICY_RESERVED_INCLUDE },
604         { "return", POLICY_RESERVED_RETURN },
605         { "module", POLICY_RESERVED_MODULE },
606         { NULL, POLICY_RESERVED_UNKNOWN }
607 };
608
609
610 /*
611  *      Simplifies some later coding
612  */
613 static int policy_lex_str2int(policy_lex_file_t *lexer,
614                               const FR_NAME_NUMBER *table, int default_value)
615 {
616         policy_lex_t token;
617         char buffer[256];
618
619         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
620         if (token != POLICY_LEX_BARE_WORD) {
621                 fprintf(stderr, "%s[%d]: Unexpected token\n",
622                         lexer->filename, lexer->lineno);
623                 return default_value;
624         }
625
626         return fr_str2int(table, buffer, default_value);
627 }
628
629
630 /*
631  *      print foo
632  *      print "foo"
633  */
634 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
635 {
636         policy_lex_t token;
637         char mystring[1024];
638         policy_print_t *this;
639
640         debug_tokens("[PRINT] ");
641
642         this = rad_malloc(sizeof(*this));
643         memset(this, 0, sizeof(*this));
644
645         this->item.type = POLICY_TYPE_PRINT;
646         this->item.lineno = lexer->lineno;
647
648         token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
649         if ((token != POLICY_LEX_BARE_WORD) &&
650             (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
651                 fprintf(stderr, "%s[%d]: Bad print command\n",
652                         lexer->filename, lexer->lineno);
653                 rlm_policy_free_item((policy_item_t *) this);
654                 return 0;
655         }
656
657         this->rhs_type = token;
658         this->rhs = strdup(mystring);
659
660         *tail = (policy_item_t *) this;
661
662         return 1;
663 }
664
665
666 /*
667  * (foo == bar), with nested conditionals.
668  */
669 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
670 {
671         int rcode, seen_not = FALSE;
672         policy_lex_t token, compare;
673         char lhs[256], rhs[256];
674         policy_condition_t *this;
675
676         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
677         if (token != POLICY_LEX_L_BRACKET) {
678                 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
679                         lexer->filename, lexer->lineno,
680                         fr_int2str(rlm_policy_tokens, token, lhs));
681                 return 0;
682         }
683
684         this = rad_malloc(sizeof(*this));
685         memset(this, 0, sizeof(*this));
686
687         this->item.type = POLICY_TYPE_CONDITIONAL;
688         this->item.lineno = lexer->lineno;
689
690  redo:
691         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
692         switch (token) {
693         case POLICY_LEX_L_BRACKET:
694                 if (!policy_lex_push_token(lexer, token)) {
695                         rlm_policy_free_item((policy_item_t *) this);
696                         return 0;
697                 }
698
699                 this->compare = POLICY_LEX_L_BRACKET;
700                 this->child_condition = POLICY_LEX_L_BRACKET;
701                 rcode = parse_condition(lexer, &(this->child));
702                 if (!rcode) {
703                         rlm_policy_free_item((policy_item_t *) this);
704                         return rcode;
705                 }
706                 break;
707
708         case POLICY_LEX_L_NOT:
709                 if (seen_not) {
710                         fprintf(stderr, "%s[%d]: Syntax error at \"!!\"\n",
711                                 lexer->filename, lexer->lineno);
712                         rlm_policy_free_item((policy_item_t *) this);
713                         return 0;
714                 }
715
716                 debug_tokens("[NOT] ");
717
718                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
719                 if (token != POLICY_LEX_L_BRACKET) {
720                         seen_not = this->sense = 1;
721                         goto redo;
722                 }
723
724                 this->compare = POLICY_LEX_L_NOT;
725                 rcode = parse_condition(lexer, &(this->child));
726                 if (!rcode) {
727                         rlm_policy_free_item((policy_item_t *) this);
728                         return rcode;
729                 }
730                 break;
731
732         case POLICY_LEX_BARE_WORD:
733                 this->lhs_type = token;
734                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
735                 if (token == POLICY_LEX_L_BRACKET) {
736                         debug_tokens("[IF-CALL %s] ", lhs);
737
738                         /*
739                          *      Function call.
740                          */
741                         if (rlm_policy_find(lexer->policies, lhs) == NULL) {
742                                 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
743                                         lexer->filename, lexer->lineno,
744                                         lhs);
745                                 rlm_policy_free_item((policy_item_t *) this);
746                                 return 0;
747
748                         }
749
750                         /*
751                          *      this->lhs set up below, after "check"
752                          */
753                         this->lhs_type = POLICY_LEX_FUNCTION;
754
755                         /*
756                          *      Copied from parse_call
757                          */
758                         token = policy_lex_file(lexer, 0, NULL, 0);
759                         if (token != POLICY_LEX_L_BRACKET) {
760                                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
761                                         lexer->filename, lexer->lineno,
762                                         fr_int2str(rlm_policy_tokens, token, "?"));
763                                 rlm_policy_free_item((policy_item_t *) this);
764                                 return 0;
765                         }
766
767                         token = policy_lex_file(lexer, 0, NULL, 0);
768                         if (token != POLICY_LEX_R_BRACKET) {
769                                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
770                                         lexer->filename, lexer->lineno,
771                                         fr_int2str(rlm_policy_tokens, token, "?"));
772                                 rlm_policy_free_item((policy_item_t *) this);
773                                 return 0;
774                         }
775                 } /* else it's a comparison? */
776                 goto check;
777
778         case POLICY_LEX_DOUBLE_QUOTED_STRING:
779                 this->lhs_type = token;
780
781                 /*
782                  *      Got word.  May just be test for existence.
783                  */
784         check:
785                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
786                 if (token == POLICY_LEX_R_BRACKET) {
787                         debug_tokens("[TEST %s] ", lhs);
788                         this->lhs = strdup(lhs);
789                         this->compare = POLICY_LEX_CMP_TRUE;
790                         break;
791                 }
792
793                 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
794                 switch (compare) {
795                 case POLICY_LEX_CMP_EQUALS:
796                 case POLICY_LEX_CMP_NOT_EQUALS:
797                 case POLICY_LEX_RX_EQUALS:
798                 case POLICY_LEX_RX_NOT_EQUALS:
799                 case POLICY_LEX_CMP_TRUE:
800                 case POLICY_LEX_CMP_FALSE:
801                 case POLICY_LEX_LT:
802                 case POLICY_LEX_GT:
803                 case POLICY_LEX_LE:
804                 case POLICY_LEX_GE:
805                         break;
806
807                 default:
808                         fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
809                                 lexer->filename, lexer->lineno,
810                                 fr_int2str(rlm_policy_tokens, compare, rhs));
811                         rlm_policy_free_item((policy_item_t *) this);
812                         return 0;
813                 }
814
815                 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
816                 if ((token != POLICY_LEX_BARE_WORD) &&
817                     (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
818                         fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
819                                 lexer->filename, lexer->lineno);
820                         rlm_policy_free_item((policy_item_t *) this);
821                         return 0;
822                 }
823                 debug_tokens("[COMPARE (%s %s %s)] ",
824                        lhs, fr_int2str(rlm_policy_tokens, compare, "?"), rhs);
825                 this->lhs = strdup(lhs);
826                 this->compare = compare;
827                 this->rhs_type = token;
828                 this->rhs = strdup(rhs);
829                 break;
830
831         default:
832                 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
833                         lexer->filename, lexer->lineno);
834                 rlm_policy_free_item((policy_item_t *) this);
835                 return 0;
836         }
837
838         token = policy_lex_file(lexer, 0, NULL, 0);
839         if (token != POLICY_LEX_R_BRACKET) {
840                 fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
841                         lexer->filename, lexer->lineno,
842                         fr_int2str(rlm_policy_tokens, token, "?"));
843                 rlm_policy_free_item((policy_item_t *) this);
844                 return 0;
845         }
846
847         /*
848          *      After the end of condition, we MAY have && or ||
849          */
850         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
851         if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
852                 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
853                 debug_tokens("[%s] ",
854                        fr_int2str(rlm_policy_tokens, token, "?"));
855                 this->child_condition = token;
856                 rcode = parse_condition(lexer, &(this->child));
857                 if (!rcode) {
858                         rlm_policy_free_item((policy_item_t *) this);
859                         return 0;
860                 }
861         }
862
863         *tail = (policy_item_t *) this;
864
865         return 1;
866 }
867
868
869 /*
870  *      if (...) {...}
871  *      if (...) {...} else {...}
872  *      if (...) {...} else if ...
873  */
874 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
875 {
876         int rcode;
877         policy_lex_t token;
878         char mystring[256];
879         policy_if_t *this;
880
881         debug_tokens("[IF] ");
882
883         this = rad_malloc(sizeof(*this));
884         memset(this, 0, sizeof(*this));
885
886         this->item.type = POLICY_TYPE_IF;
887         this->item.lineno = lexer->lineno;
888
889         rcode = parse_condition(lexer, &(this->condition));
890         if (!rcode) {
891                 rlm_policy_free_item((policy_item_t *) this);
892                 return rcode;
893         }
894
895         rcode = parse_block(lexer, &(this->if_true));
896         if (!rcode) {
897                 rlm_policy_free_item((policy_item_t *) this);
898                 return rcode;
899         }
900
901         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
902                                 mystring, sizeof(mystring));
903         if ((token == POLICY_LEX_BARE_WORD) &&
904             (fr_str2int(policy_reserved_words, mystring,
905                           POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
906                 debug_tokens("[ELSE] ");
907                 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
908                 rad_assert(token == POLICY_LEX_BARE_WORD);
909
910                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
911                                         mystring, sizeof(mystring));
912                 if ((token == POLICY_LEX_BARE_WORD) &&
913                     (fr_str2int(policy_reserved_words, mystring,
914                                   POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
915                         token = policy_lex_file(lexer, 0,
916                                                 mystring, sizeof(mystring));
917                         rad_assert(token == POLICY_LEX_BARE_WORD);
918                         rcode = parse_if(lexer, &(this->if_false));
919                 } else {
920                         rcode = parse_block(lexer, &(this->if_false));
921                 }
922                 if (!rcode) {
923                         rlm_policy_free_item((policy_item_t *) this);
924                         return rcode;
925                 }
926         }
927
928         debug_tokens("\n");
929
930         /*
931          *      Empty "if" condition, don't even bother remembering
932          *      it.
933          */
934         if (!this->if_true && !this->if_false) {
935                 debug_tokens("Discarding empty \"if\" statement at line %d\n",
936                              this->item.lineno);
937                 rlm_policy_free_item((policy_item_t *) this);
938                 return 1;
939         }
940
941         *tail = (policy_item_t *) this;
942
943         return 1;
944 }
945
946
947 /*
948  *      Parse a reference to a named policy "foo()"
949  */
950 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
951                       const char *name)
952 {
953         policy_lex_t token;
954         policy_call_t *this;
955
956         debug_tokens("[CALL] ");
957
958         token = policy_lex_file(lexer, 0, NULL, 0);
959         if (token != POLICY_LEX_L_BRACKET) {
960                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
961                         lexer->filename, lexer->lineno,
962                         fr_int2str(rlm_policy_tokens, token, "?"));
963                 return 0;
964         }
965
966         token = policy_lex_file(lexer, 0, NULL, 0);
967         if (token != POLICY_LEX_R_BRACKET) {
968                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
969                         lexer->filename, lexer->lineno,
970                         fr_int2str(rlm_policy_tokens, token, "?"));
971                 return 0;
972         }
973
974         this = rad_malloc(sizeof(*this));
975         memset(this, 0, sizeof(*this));
976
977         this->item.type = POLICY_TYPE_CALL;
978         this->item.lineno = lexer->lineno;
979
980         this->name = strdup(name);
981
982         *tail = (policy_item_t *) this;
983
984         return 1;
985 }
986
987
988 /*
989  *      Edit/update/replace an attribute list
990  */
991 static int parse_attribute_block(policy_lex_file_t *lexer,
992                                  policy_item_t **tail,
993                                  policy_reserved_word_t where)
994 {
995         policy_lex_t token;
996         policy_attributes_t *this;
997         char buffer[32];
998
999         this = rad_malloc(sizeof(*this));
1000         if (!this) {
1001                 return 0;
1002         }
1003         memset(this, 0, sizeof(*this));
1004         this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
1005         this->item.lineno = lexer->lineno;
1006         this->where = where;
1007
1008         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1009         this->how = token;
1010
1011         switch (token) {
1012         case POLICY_LEX_BEFORE_WHERE_EQUALS:
1013         case POLICY_LEX_AFTER_WHERE_EQUALS:
1014         case POLICY_LEX_BEFORE_WHERE_ASSIGN:
1015         case POLICY_LEX_AFTER_WHERE_ASSIGN:
1016                 if (!parse_condition(lexer, &(this->where_loc))) {
1017                         rlm_policy_free_item((policy_item_t *)this);
1018                         return 0;
1019                 }
1020                 break;
1021         case POLICY_LEX_BEFORE_HEAD_EQUALS:
1022         case POLICY_LEX_AFTER_TAIL_EQUALS:
1023         case POLICY_LEX_BEFORE_HEAD_ASSIGN:
1024         case POLICY_LEX_AFTER_TAIL_ASSIGN:
1025         case POLICY_LEX_ASSIGN:
1026         case POLICY_LEX_SET_EQUALS:
1027         case POLICY_LEX_CONCAT_EQUALS:
1028                 break;
1029
1030         default:
1031                 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
1032                         lexer->filename, lexer->lineno,
1033                         fr_int2str(rlm_policy_tokens, token, "?"));
1034                 rlm_policy_free_item((policy_item_t *)this);
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((policy_item_t *) 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