2 * parse.c Parse a policy language
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2004 Alan DeKok <aland@ox.org>
21 * Copyright 2006 The FreeRADIUS server project
24 #include <freeradius-devel/ident.h>
27 #include "rlm_policy.h"
32 #ifdef HAVE_SYS_STAT_H
37 #include <freeradius-devel/modules.h>
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 }
53 * Explanations of what the lexical tokens are.
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 },
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 },
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.
137 * Lexical tokens cannot cross a string boundary.
139 static const char *policy_lex_string(const char *input,
141 char *buffer, size_t buflen)
143 rad_assert(input != NULL);
147 *token = POLICY_LEX_EOL;
148 return NULL; /* nothing more to do */
155 * Skip over all of the whitespace in one swell foop.
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 */
162 case '#': /* ignore everything to the end of the line */
163 *token = POLICY_LEX_EOL;
167 *token = POLICY_LEX_L_BRACKET;
171 *token = POLICY_LEX_R_BRACKET;
175 *token = POLICY_LEX_LC_BRACKET;
179 *token = POLICY_LEX_RC_BRACKET;
183 *token = POLICY_LEX_COMMA;
189 *token = POLICY_LEX_PLUS_EQUALS;
194 *token = POLICY_LEX_PLUS;
202 *token = POLICY_LEX_MINUS_EQUALS;
207 *token = POLICY_LEX_MINUS;
213 if (input[1] == '=') {
214 *token = POLICY_LEX_CONCAT_EQUALS;
217 *token = POLICY_LEX_BAD;
221 if (input[1] == '.' ) {
222 if (input[2] == '=') {
223 *token = POLICY_LEX_BEFORE_WHERE_EQUALS;
227 *token = POLICY_LEX_BEFORE_HEAD_EQUALS;
231 else if (input[1] == '=') {
232 if (input[2] == '=') {
233 *token = POLICY_LEX_BEFORE_WHERE_ASSIGN;
237 *token = POLICY_LEX_BEFORE_HEAD_ASSIGN;
242 *token = POLICY_LEX_BAD;
246 if (input[1] == '.' ) {
247 if (input[2] == '=') {
248 *token = POLICY_LEX_AFTER_WHERE_EQUALS;
252 *token = POLICY_LEX_AFTER_TAIL_EQUALS;
256 else if (input[1] == '=') {
257 if (input[2] == '=') {
258 *token = POLICY_LEX_AFTER_WHERE_ASSIGN;
262 *token = POLICY_LEX_AFTER_TAIL_ASSIGN;
267 *token = POLICY_LEX_BAD;
271 if (input[1] == '=') {
272 *token = POLICY_LEX_SET_EQUALS;
275 *token = POLICY_LEX_BAD;
281 *token = POLICY_LEX_L_AND;
286 *token = POLICY_LEX_AND_EQUALS;
291 *token = POLICY_LEX_AND;
298 *token = POLICY_LEX_L_OR;
303 *token = POLICY_LEX_OR_EQUALS;
308 *token = POLICY_LEX_OR;
316 *token = POLICY_LEX_CMP_NOT_EQUALS;
321 *token = POLICY_LEX_RX_NOT_EQUALS;
326 *token = POLICY_LEX_CMP_FALSE;
330 *token = POLICY_LEX_L_NOT;
338 *token = POLICY_LEX_CMP_EQUALS;
343 *token = POLICY_LEX_RX_EQUALS;
348 *token = POLICY_LEX_CMP_TRUE;
352 *token = POLICY_LEX_ASSIGN;
357 if (input[1] == '=') {
359 *token = POLICY_LEX_LE;
361 *token = POLICY_LEX_LT;
366 if (input[1] == '=') {
368 *token = POLICY_LEX_GE;
370 *token = POLICY_LEX_GT;
375 if (!buffer || (buflen < 2)) {
376 *token = POLICY_LEX_BAD;
381 while (*input != '"') {
383 * Strings can't pass EOL.
386 return POLICY_LEX_BAD;
390 * FIXME: Embedded quotes?
392 *(buffer++) = *(input++);
396 * FIXME: Print more warnings?
404 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
405 return input + 1; /* skip trailing '"' */
407 default: /* bare word */
412 * It's a bare word, with nowhere to put it. Die.
415 *token = POLICY_LEX_BAD;
420 * Getting one character is stupid.
423 *token = POLICY_LEX_BAD;
428 * Bare words are [-a-zA-Z0-9.]+
431 if (!(((*input >= '0') && (*input <= '9')) ||
432 ((*input >= 'a') && (*input <= 'z')) ||
433 ((*input >= 'A') && (*input <= 'Z')) ||
434 (*input == '-') || (*input == '.') ||
435 (*input == ':') || (*input == '_'))) {
438 *(buffer++) = *(input++);
442 * FIXME: Print more warnings?
450 *token = POLICY_LEX_BARE_WORD;
456 * We want to lexically analyze a file, so we need a wrapper
457 * around the lexical analysis of strings.
459 typedef struct policy_lex_file_t {
462 const char *filename;
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)
475 #define debug_tokens if ((lexer->debug & POLICY_DEBUG_PRINT_TOKENS) && fr_log_fp) fr_printf_log
479 * Function to return a token saying what it read, and possibly
480 * a buffer of the quoted string or bare word.
482 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
484 char *mystring, size_t mystringlen)
486 policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
488 if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
489 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
493 return POLICY_LEX_EOF;
497 * Starting off, the buffer needs to be primed.
500 lexer->parse = fgets(lexer->buffer,
501 sizeof(lexer->buffer),
505 return POLICY_LEX_EOF;
509 } /* buffer is primed, read stuff */
511 if (lexer->token != POLICY_LEX_BAD) {
512 token = lexer->token;
513 lexer->token = POLICY_LEX_BAD;
518 * Ignore whitespace, and keep filling the buffer
520 while (lexer->parse) {
523 next = policy_lex_string(lexer->parse, &token,
524 mystring, mystringlen);
526 case POLICY_LEX_WHITESPACE: /* skip whitespace */
530 case POLICY_LEX_EOL: /* read another line */
531 lexer->parse = fgets(lexer->buffer,
532 sizeof(lexer->buffer),
535 if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
536 return POLICY_LEX_EOL;
538 break; /* read another token */
540 default: /* return the token */
541 if (!(flags & POLICY_LEX_FLAG_PEEK)) {
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,
553 } /* loop until EOF */
556 * Close it for the user.
561 return POLICY_LEX_EOF;
566 * Push a token back onto the input.
568 * FIXME: Push words, too?
570 static int policy_lex_push_token(policy_lex_file_t *lexer,
573 if (lexer->token != POLICY_LEX_BAD) {
578 lexer->token = token;
584 * Forward declarations.
586 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
590 * Map reserved words to tokens, and vice versa.
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 }
611 * Simplifies some later coding
613 static int policy_lex_str2int(policy_lex_file_t *lexer,
614 const FR_NAME_NUMBER *table, int default_value)
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;
626 return fr_str2int(table, buffer, default_value);
634 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
638 policy_print_t *this;
640 debug_tokens("[PRINT] ");
642 this = rad_malloc(sizeof(*this));
643 memset(this, 0, sizeof(*this));
645 this->item.type = POLICY_TYPE_PRINT;
646 this->item.lineno = lexer->lineno;
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);
657 this->rhs_type = token;
658 this->rhs = strdup(mystring);
660 *tail = (policy_item_t *) this;
667 * (foo == bar), with nested conditionals.
669 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
671 int rcode, seen_not = FALSE;
672 policy_lex_t token, compare;
673 char lhs[256], rhs[256];
674 policy_condition_t *this;
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));
684 this = rad_malloc(sizeof(*this));
685 memset(this, 0, sizeof(*this));
687 this->item.type = POLICY_TYPE_CONDITIONAL;
688 this->item.lineno = lexer->lineno;
691 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
693 case POLICY_LEX_L_BRACKET:
694 if (!policy_lex_push_token(lexer, token)) {
695 rlm_policy_free_item((policy_item_t *) this);
699 this->compare = POLICY_LEX_L_BRACKET;
700 this->child_condition = POLICY_LEX_L_BRACKET;
701 rcode = parse_condition(lexer, &(this->child));
703 rlm_policy_free_item((policy_item_t *) this);
708 case POLICY_LEX_L_NOT:
710 fprintf(stderr, "%s[%d]: Syntax error at \"!!\"\n",
711 lexer->filename, lexer->lineno);
712 rlm_policy_free_item((policy_item_t *) this);
716 debug_tokens("[NOT] ");
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;
724 this->compare = POLICY_LEX_L_NOT;
725 rcode = parse_condition(lexer, &(this->child));
727 rlm_policy_free_item((policy_item_t *) this);
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);
741 if (rlm_policy_find(lexer->policies, lhs) == NULL) {
742 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
743 lexer->filename, lexer->lineno,
745 rlm_policy_free_item((policy_item_t *) this);
751 * this->lhs set up below, after "check"
753 this->lhs_type = POLICY_LEX_FUNCTION;
756 * Copied from parse_call
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);
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);
775 } /* else it's a comparison? */
778 case POLICY_LEX_DOUBLE_QUOTED_STRING:
779 this->lhs_type = token;
782 * Got word. May just be test for existence.
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;
793 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
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:
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);
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);
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);
832 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
833 lexer->filename, lexer->lineno);
834 rlm_policy_free_item((policy_item_t *) this);
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);
848 * After the end of condition, we MAY have && or ||
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));
858 rlm_policy_free_item((policy_item_t *) this);
863 *tail = (policy_item_t *) this;
871 * if (...) {...} else {...}
872 * if (...) {...} else if ...
874 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
881 debug_tokens("[IF] ");
883 this = rad_malloc(sizeof(*this));
884 memset(this, 0, sizeof(*this));
886 this->item.type = POLICY_TYPE_IF;
887 this->item.lineno = lexer->lineno;
889 rcode = parse_condition(lexer, &(this->condition));
891 rlm_policy_free_item((policy_item_t *) this);
895 rcode = parse_block(lexer, &(this->if_true));
897 rlm_policy_free_item((policy_item_t *) this);
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);
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));
920 rcode = parse_block(lexer, &(this->if_false));
923 rlm_policy_free_item((policy_item_t *) this);
931 * Empty "if" condition, don't even bother remembering
934 if (!this->if_true && !this->if_false) {
935 debug_tokens("Discarding empty \"if\" statement at line %d\n",
937 rlm_policy_free_item((policy_item_t *) this);
941 *tail = (policy_item_t *) this;
948 * Parse a reference to a named policy "foo()"
950 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
956 debug_tokens("[CALL] ");
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, "?"));
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, "?"));
974 this = rad_malloc(sizeof(*this));
975 memset(this, 0, sizeof(*this));
977 this->item.type = POLICY_TYPE_CALL;
978 this->item.lineno = lexer->lineno;
980 this->name = strdup(name);
982 *tail = (policy_item_t *) this;
989 * Edit/update/replace an attribute list
991 static int parse_attribute_block(policy_lex_file_t *lexer,
992 policy_item_t **tail,
993 policy_reserved_word_t where)
996 policy_attributes_t *this;
999 this = rad_malloc(sizeof(*this));
1003 memset(this, 0, sizeof(*this));
1004 this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
1005 this->item.lineno = lexer->lineno;
1006 this->where = where;
1008 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
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);
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:
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 */
1038 if (!parse_block(lexer, &(this->attributes))) {
1039 rlm_policy_free_item((policy_item_t *) this);
1043 *tail = (policy_item_t *) this;
1049 * Parse a return statement.
1051 static int parse_return(policy_lex_file_t *lexer, policy_item_t **tail)
1055 policy_return_t *this;
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);
1066 * Look for more sutff
1068 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
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);
1076 this = rad_malloc(sizeof(*this));
1077 memset(this, 0, sizeof(*this));
1079 this->item.type = POLICY_TYPE_RETURN;
1080 this->item.lineno = lexer->lineno;
1081 this->rcode = rcode;
1083 *tail = (policy_item_t *) this;
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 },
1099 { "recv-coa", RLM_COMPONENT_RECV_COA },
1100 { "send-coa", RLM_COMPONENT_SEND_COA },
1102 { NULL, RLM_COMPONENT_COUNT }
1106 * Parse a module statement.
1108 static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
1112 policy_module_t *this;
1114 const char *section_name;
1115 char filename[1024];
1117 CONF_SECTION *cs, *subcs;
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, "?"));
1132 * See if we're including all of the files in a subdirectory.
1134 strlcpy(buffer, lexer->filename, sizeof(buffer));
1135 p = strrchr(buffer, '/');
1137 strlcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1139 snprintf(buffer, sizeof(buffer), "%s/%s",
1140 radius_dir, filename);
1144 * Include section calling a module.
1146 debug_tokens("including module section from file %s\n", buffer);
1147 cs = cf_file_read(buffer);
1149 return 0; /* it prints out error messages */
1153 * The outer section is called "main", and can be ignored.
1154 * It should be a section, so there should be a subsection.
1156 subcs = cf_subsection_find_next(cs, NULL, NULL);
1158 fprintf(stderr, "%s[%d]: Expected section containing modules\n",
1159 lexer->filename, lexer->lineno);
1160 cf_section_free(&cs);
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);
1176 * Compile the module entry.
1178 mc = compile_modgroup(NULL, component, subcs);
1180 cf_section_free(&cs);
1181 return 0; /* more often results in calling exit... */
1184 this = rad_malloc(sizeof(*this));
1185 memset(this, 0, sizeof(*this));
1187 this->item.type = POLICY_TYPE_MODULE;
1188 this->item.lineno = lexer->lineno;
1189 this->component = component;
1193 *tail = (policy_item_t *) this;
1200 * Parse one statement. 'foo = bar', or 'if (...) {...}', or '{...}',
1203 static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
1206 policy_reserved_word_t reserved;
1207 policy_lex_t token, assign;
1208 char lhs[256], rhs[256];
1209 policy_assignment_t *this;
1212 * See what kind of token we have.
1214 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
1216 case POLICY_LEX_LC_BRACKET:
1217 rcode = parse_block(lexer, tail);
1223 case POLICY_LEX_BARE_WORD:
1224 reserved = fr_str2int(policy_reserved_words,
1226 POLICY_RESERVED_UNKNOWN);
1228 case POLICY_RESERVED_IF:
1229 if (parse_if(lexer, tail)) {
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,
1246 case POLICY_RESERVED_PRINT:
1247 if (parse_print(lexer, tail)) {
1253 case POLICY_RESERVED_RETURN:
1254 if (parse_return(lexer, tail)) {
1260 case POLICY_RESERVED_MODULE:
1261 if (parse_module(lexer, tail)) {
1267 case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
1269 * Is a named policy, parse the reference to it.
1271 if (rlm_policy_find(lexer->policies, lhs) != NULL) {
1272 if (!parse_call(lexer, tail, lhs)) {
1279 const DICT_ATTR *dattr;
1282 * Bare words MUST be dictionary attributes
1285 dattr = dict_attrbyname(lhs);
1287 fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
1288 lexer->filename, lexer->lineno, lhs);
1291 debug_tokens("%s[%d]: Got attribute %s\n",
1292 lexer->filename, lexer->lineno,
1298 fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
1299 lexer->filename, lexer->lineno, lhs);
1301 } /* switch over reserved words */
1305 * Return from nested blocks.
1307 case POLICY_LEX_RC_BRACKET:
1308 policy_lex_push_token(lexer, token);
1309 return 2; /* magic */
1311 case POLICY_LEX_EOF: /* nothing more to do */
1315 fprintf(stderr, "%s[%d]: Unexpected %s\n",
1316 lexer->filename, lexer->lineno,
1317 fr_int2str(policy_explanations,
1323 * Parse a bare statement.
1325 assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
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:
1335 fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
1336 lexer->filename, lexer->lineno,
1337 fr_int2str(policy_explanations,
1342 this = rad_malloc(sizeof(*this));
1343 memset(this, 0, sizeof(*this));
1345 this->item.type = POLICY_TYPE_ASSIGNMENT;
1346 this->item.lineno = lexer->lineno;
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,
1355 rlm_policy_free_item((policy_item_t *) this);
1358 this->rhs_type = token;
1359 this->rhs = strdup(rhs);
1361 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
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);
1369 debug_tokens("[ASSIGN %s %s %s]\n",
1370 lhs, fr_int2str(rlm_policy_tokens, assign, "?"), rhs);
1373 * Fill in the assignment struct
1375 this->lhs = strdup(lhs);
1376 this->assign = assign;
1378 *tail = (policy_item_t *) this;
1385 * Parse block of statements. The block has already been checked
1386 * to begin with a '{'.
1388 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail)
1393 debug_tokens("[BLOCK] ");
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);
1402 while ((rcode = parse_statement(lexer, tail)) != 0) {
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);
1412 rad_assert(*tail != NULL);
1413 /* parse_statement must fill this in */
1414 while (*tail) tail = &((*tail)->next);
1419 * Parse statement failed.
1426 * Parse debugging statements
1428 static int parse_debug(policy_lex_file_t *lexer)
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);
1441 if (strcasecmp(buffer, "none") == 0) {
1442 lexer->debug = POLICY_DEBUG_NONE;
1445 } else if (strcasecmp(buffer, "peek") == 0) {
1446 lexer->debug |= POLICY_DEBUG_PEEK;
1449 } else if (strcasecmp(buffer, "print_tokens") == 0) {
1450 lexer->debug |= POLICY_DEBUG_PRINT_TOKENS;
1453 } else if (strcasecmp(buffer, "print_policy") == 0) {
1454 lexer->debug |= POLICY_DEBUG_PRINT_POLICY;
1457 } else if (strcasecmp(buffer, "evaluate") == 0) {
1458 lexer->debug |= POLICY_DEBUG_EVALUATE;
1463 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1465 if (token != POLICY_LEX_EOL) {
1466 fprintf(stderr, "%s[%d]: Expected EOL\n",
1467 lexer->filename, lexer->lineno);
1471 fprintf(stderr, "%s[%d]: Bad debug command \"%s\"\n",
1472 lexer->filename, lexer->lineno, buffer);
1481 * Parse a named policy "policy foo {...}"
1483 static int parse_named_policy(policy_lex_file_t *lexer)
1488 policy_named_t *this;
1491 debug_tokens("[POLICY] ");
1493 this = rad_malloc(sizeof(*this));
1494 memset(this, 0, sizeof(*this));
1496 this->item.type = POLICY_TYPE_NAMED_POLICY;
1497 this->item.lineno = lexer->lineno;
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);
1508 dattr = dict_attrbyname(mystring);
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);
1516 this->name = strdup(mystring);
1517 rcode = parse_block(lexer, &(this->policy));
1519 rlm_policy_free_item((policy_item_t *) this);
1524 * And insert it into the tree of policies.
1526 * For now, policy names aren't scoped, they're global.
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);
1534 if ((lexer->debug & POLICY_DEBUG_PRINT_POLICY) != 0) {
1535 rlm_policy_print((policy_item_t *) this);
1543 * Parse an "include filename" statement
1545 * FIXME: Tie this file into the CONF_SECTION for HUP handling!
1547 static int parse_include(policy_lex_file_t *lexer)
1551 char filename[1024];
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, "?"));
1563 * See if we're including all of the files in a subdirectory.
1565 strlcpy(buffer, lexer->filename, sizeof(buffer));
1566 p = strrchr(buffer, '/');
1568 strlcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1570 #ifdef HAVE_DIRENT_H
1571 p = strrchr(p + 1, '/');
1578 dir = opendir(buffer);
1580 fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
1581 lexer->filename, lexer->lineno,
1582 buffer, strerror(errno));
1587 * Read the directory, ignoring "." files.
1589 while ((dp = readdir(dir)) != NULL) {
1592 if (dp->d_name[0] == '.') continue;
1593 if (strchr(dp->d_name, '~') != NULL) continue;
1595 strlcpy(p, dp->d_name,
1596 sizeof(buffer) - (p - buffer));
1598 if ((stat(buffer, &buf) != 0) ||
1599 S_ISDIR(buf.st_mode)) continue;
1601 debug_tokens("\nincluding file %s\n", buffer);
1602 if (!rlm_policy_parse(lexer->policies, buffer)) {
1609 } /* else it must have been a normalx file */
1612 snprintf(buffer, sizeof(buffer), "%s/%s",
1613 radius_dir, filename);
1617 * Handle one include file.
1619 debug_tokens("\nincluding file %s\n", buffer);
1620 if (!rlm_policy_parse(lexer->policies, buffer)) {
1629 * Parse data from a file into a policy language.
1631 int rlm_policy_parse(rbtree_t *policies, const char *filename)
1635 policy_lex_file_t mylexer, *lexer = NULL;
1638 fp = fopen(filename, "r");
1640 fprintf(stderr, "Failed to open %s: %s\n",
1641 filename, strerror(errno));
1646 memset(lexer, 0, sizeof(*lexer));
1647 lexer->filename = filename;
1649 lexer->token = POLICY_LEX_BAD;
1650 lexer->parse = NULL; /* initial input */
1651 lexer->policies = policies;
1656 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1658 case POLICY_LEX_BARE_WORD:
1659 reserved = fr_str2int(policy_reserved_words,
1661 POLICY_RESERVED_UNKNOWN);
1663 case POLICY_RESERVED_POLICY:
1664 if (!parse_named_policy(lexer)) {
1669 case POLICY_RESERVED_INCLUDE:
1670 if (!parse_include(lexer)) {
1675 case POLICY_RESERVED_DEBUG:
1676 if (!parse_debug(lexer)) {
1682 fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
1683 lexer->filename, lexer->lineno,
1687 } /* switch over reserved words */
1689 case POLICY_LEX_EOF:
1693 fprintf(stderr, "%s[%d]: Illegal input\n",
1694 lexer->filename, lexer->lineno);
1697 } while (token != POLICY_LEX_EOF);
1699 if (((lexer->debug & POLICY_DEBUG_PRINT_POLICY) != 0) && fr_log_fp) {
1700 fprintf(fr_log_fp, "# rlm_policy \n");
1703 debug_tokens("--------------------------------------------------\n");