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);
144 rad_assert(buffer != NULL);
148 *token = POLICY_LEX_EOL;
149 return NULL; /* nothing more to do */
156 * Skip over all of the whitespace in one swell foop.
158 *token = POLICY_LEX_WHITESPACE;
159 while ((*input == ' ') || (*input == '\t') ||
160 (*input == '\r') || (*input == '\n')) input++;
161 return input; /* point to next non-whitespace character */
163 case '#': /* ignore everything to the end of the line */
164 *token = POLICY_LEX_EOL;
168 *token = POLICY_LEX_L_BRACKET;
172 *token = POLICY_LEX_R_BRACKET;
176 *token = POLICY_LEX_LC_BRACKET;
180 *token = POLICY_LEX_RC_BRACKET;
184 *token = POLICY_LEX_COMMA;
190 *token = POLICY_LEX_PLUS_EQUALS;
195 *token = POLICY_LEX_PLUS;
203 *token = POLICY_LEX_MINUS_EQUALS;
208 *token = POLICY_LEX_MINUS;
214 if (input[1] == '=') {
215 *token = POLICY_LEX_CONCAT_EQUALS;
218 *token = POLICY_LEX_BAD;
222 if (input[1] == '.' ) {
223 if (input[2] == '=') {
224 *token = POLICY_LEX_BEFORE_WHERE_EQUALS;
228 *token = POLICY_LEX_BEFORE_HEAD_EQUALS;
232 else if (input[1] == '=') {
233 if (input[2] == '=') {
234 *token = POLICY_LEX_BEFORE_WHERE_ASSIGN;
238 *token = POLICY_LEX_BEFORE_HEAD_ASSIGN;
243 *token = POLICY_LEX_BAD;
247 if (input[1] == '.' ) {
248 if (input[2] == '=') {
249 *token = POLICY_LEX_AFTER_WHERE_EQUALS;
253 *token = POLICY_LEX_AFTER_TAIL_EQUALS;
257 else if (input[1] == '=') {
258 if (input[2] == '=') {
259 *token = POLICY_LEX_AFTER_WHERE_ASSIGN;
263 *token = POLICY_LEX_AFTER_TAIL_ASSIGN;
268 *token = POLICY_LEX_BAD;
272 if (input[1] == '=') {
273 *token = POLICY_LEX_SET_EQUALS;
276 *token = POLICY_LEX_BAD;
282 *token = POLICY_LEX_L_AND;
287 *token = POLICY_LEX_AND_EQUALS;
292 *token = POLICY_LEX_AND;
299 *token = POLICY_LEX_L_OR;
304 *token = POLICY_LEX_OR_EQUALS;
309 *token = POLICY_LEX_OR;
317 *token = POLICY_LEX_CMP_NOT_EQUALS;
322 *token = POLICY_LEX_RX_NOT_EQUALS;
327 *token = POLICY_LEX_CMP_FALSE;
331 *token = POLICY_LEX_L_NOT;
339 *token = POLICY_LEX_CMP_EQUALS;
344 *token = POLICY_LEX_RX_EQUALS;
349 *token = POLICY_LEX_CMP_TRUE;
353 *token = POLICY_LEX_ASSIGN;
358 if (input[1] == '=') {
360 *token = POLICY_LEX_LE;
362 *token = POLICY_LEX_LT;
367 if (input[1] == '=') {
369 *token = POLICY_LEX_GE;
371 *token = POLICY_LEX_GT;
377 *token = POLICY_LEX_BAD;
382 while (*input != '"') {
384 * Strings can't pass EOL.
387 return POLICY_LEX_BAD;
391 * FIXME: Embedded quotes?
393 *(buffer++) = *(input++);
397 * FIXME: Print more warnings?
405 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
406 return input + 1; /* skip trailing '"' */
408 default: /* bare word */
413 * It's a bare word, with nowhere to put it. Die.
416 *token = POLICY_LEX_BAD;
421 * Getting one character is stupid.
424 *token = POLICY_LEX_BAD;
429 * Bare words are [-a-zA-Z0-9.]+
432 if (!(((*input >= '0') && (*input <= '9')) ||
433 ((*input >= 'a') && (*input <= 'z')) ||
434 ((*input >= 'A') && (*input <= 'Z')) ||
435 (*input == '-') || (*input == '.') ||
436 (*input == ':') || (*input == '_'))) {
439 *(buffer++) = *(input++);
443 * FIXME: Print more warnings?
451 *token = POLICY_LEX_BARE_WORD;
457 * We want to lexically analyze a file, so we need a wrapper
458 * around the lexical analysis of strings.
460 typedef struct policy_lex_file_t {
463 const char *filename;
472 #define POLICY_LEX_FLAG_RETURN_EOL (1 << 0)
473 #define POLICY_LEX_FLAG_PEEK (1 << 1)
474 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
476 #define debug_tokens if ((lexer->debug & POLICY_DEBUG_PRINT_TOKENS) && fr_log_fp) fr_printf_log
480 * Function to return a token saying what it read, and possibly
481 * a buffer of the quoted string or bare word.
483 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
485 char *mystring, size_t mystringlen)
487 policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
489 if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
490 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
494 return POLICY_LEX_EOF;
498 * Starting off, the buffer needs to be primed.
501 lexer->parse = fgets(lexer->buffer,
502 sizeof(lexer->buffer),
506 return POLICY_LEX_EOF;
510 } /* buffer is primed, read stuff */
512 if (lexer->token != POLICY_LEX_BAD) {
513 token = lexer->token;
514 lexer->token = POLICY_LEX_BAD;
519 * Ignore whitespace, and keep filling the buffer
521 while (lexer->parse) {
524 next = policy_lex_string(lexer->parse, &token,
525 mystring, mystringlen);
527 case POLICY_LEX_WHITESPACE: /* skip whitespace */
531 case POLICY_LEX_EOL: /* read another line */
532 lexer->parse = fgets(lexer->buffer,
533 sizeof(lexer->buffer),
536 if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
537 return POLICY_LEX_EOL;
539 break; /* read another token */
541 default: /* return the token */
542 if (!(flags & POLICY_LEX_FLAG_PEEK)) {
545 if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
546 debug_tokens("[%s token %s] ",
547 (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
548 fr_int2str(rlm_policy_tokens,
554 } /* loop until EOF */
557 * Close it for the user.
562 return POLICY_LEX_EOF;
567 * Push a token back onto the input.
569 * FIXME: Push words, too?
571 static int policy_lex_push_token(policy_lex_file_t *lexer,
574 if (lexer->token != POLICY_LEX_BAD) {
579 lexer->token = token;
585 * Forward declarations.
587 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
591 * Map reserved words to tokens, and vice versa.
593 const FR_NAME_NUMBER policy_reserved_words[] = {
594 { "if", POLICY_RESERVED_IF },
595 { "else", POLICY_RESERVED_ELSE },
596 { "debug", POLICY_RESERVED_DEBUG },
597 { "print", POLICY_RESERVED_PRINT },
598 { "policy", POLICY_RESERVED_POLICY },
599 { "control", POLICY_RESERVED_CONTROL },
600 { "request", POLICY_RESERVED_REQUEST },
601 { "reply", POLICY_RESERVED_REPLY },
602 { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
603 { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
604 { "include", POLICY_RESERVED_INCLUDE },
605 { "return", POLICY_RESERVED_RETURN },
606 { "module", POLICY_RESERVED_MODULE },
607 { NULL, POLICY_RESERVED_UNKNOWN }
612 * Simplifies some later coding
614 static int policy_lex_str2int(policy_lex_file_t *lexer,
615 const FR_NAME_NUMBER *table, int default_value)
620 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
621 if (token != POLICY_LEX_BARE_WORD) {
622 fprintf(stderr, "%s[%d]: Unexpected token\n",
623 lexer->filename, lexer->lineno);
624 return default_value;
627 return fr_str2int(table, buffer, default_value);
635 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
639 policy_print_t *this;
641 debug_tokens("[PRINT] ");
643 this = rad_malloc(sizeof(*this));
644 memset(this, 0, sizeof(*this));
646 this->item.type = POLICY_TYPE_PRINT;
647 this->item.lineno = lexer->lineno;
649 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
650 if ((token != POLICY_LEX_BARE_WORD) &&
651 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
652 fprintf(stderr, "%s[%d]: Bad print command\n",
653 lexer->filename, lexer->lineno);
654 rlm_policy_free_item((policy_item_t *) this);
658 this->rhs_type = token;
659 this->rhs = strdup(mystring);
661 *tail = (policy_item_t *) this;
668 * (foo == bar), with nested conditionals.
670 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
672 int rcode, seen_not = FALSE;
673 policy_lex_t token, compare;
674 char lhs[256], rhs[256];
675 policy_condition_t *this;
677 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
678 if (token != POLICY_LEX_L_BRACKET) {
679 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
680 lexer->filename, lexer->lineno,
681 fr_int2str(rlm_policy_tokens, token, lhs));
685 this = rad_malloc(sizeof(*this));
686 memset(this, 0, sizeof(*this));
688 this->item.type = POLICY_TYPE_CONDITIONAL;
689 this->item.lineno = lexer->lineno;
692 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
694 case POLICY_LEX_L_BRACKET:
695 if (!policy_lex_push_token(lexer, token)) {
696 rlm_policy_free_item((policy_item_t *) this);
700 this->compare = POLICY_LEX_L_BRACKET;
701 this->child_condition = POLICY_LEX_L_BRACKET;
702 rcode = parse_condition(lexer, &(this->child));
704 rlm_policy_free_item((policy_item_t *) this);
709 case POLICY_LEX_L_NOT:
711 fprintf(stderr, "%s[%d]: Syntax error at \"!!\"\n",
712 lexer->filename, lexer->lineno);
713 rlm_policy_free_item((policy_item_t *) this);
717 debug_tokens("[NOT] ");
719 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
720 if (token != POLICY_LEX_L_BRACKET) {
721 seen_not = this->sense = 1;
725 this->compare = POLICY_LEX_L_NOT;
726 rcode = parse_condition(lexer, &(this->child));
728 rlm_policy_free_item((policy_item_t *) this);
733 case POLICY_LEX_BARE_WORD:
734 this->lhs_type = token;
735 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
736 if (token == POLICY_LEX_L_BRACKET) {
737 debug_tokens("[IF-CALL %s] ", lhs);
742 if (rlm_policy_find(lexer->policies, lhs) == NULL) {
743 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
744 lexer->filename, lexer->lineno,
746 rlm_policy_free_item((policy_item_t *) this);
752 * this->lhs set up below, after "check"
754 this->lhs_type = POLICY_LEX_FUNCTION;
757 * Copied from parse_call
759 token = policy_lex_file(lexer, 0, NULL, 0);
760 if (token != POLICY_LEX_L_BRACKET) {
761 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
762 lexer->filename, lexer->lineno,
763 fr_int2str(rlm_policy_tokens, token, "?"));
764 rlm_policy_free_item((policy_item_t *) this);
768 token = policy_lex_file(lexer, 0, NULL, 0);
769 if (token != POLICY_LEX_R_BRACKET) {
770 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
771 lexer->filename, lexer->lineno,
772 fr_int2str(rlm_policy_tokens, token, "?"));
773 rlm_policy_free_item((policy_item_t *) this);
776 } /* else it's a comparison? */
779 case POLICY_LEX_DOUBLE_QUOTED_STRING:
780 this->lhs_type = token;
783 * Got word. May just be test for existence.
786 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
787 if (token == POLICY_LEX_R_BRACKET) {
788 debug_tokens("[TEST %s] ", lhs);
789 this->lhs = strdup(lhs);
790 this->compare = POLICY_LEX_CMP_TRUE;
794 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
796 case POLICY_LEX_CMP_EQUALS:
797 case POLICY_LEX_CMP_NOT_EQUALS:
798 case POLICY_LEX_RX_EQUALS:
799 case POLICY_LEX_RX_NOT_EQUALS:
800 case POLICY_LEX_CMP_TRUE:
801 case POLICY_LEX_CMP_FALSE:
809 fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
810 lexer->filename, lexer->lineno,
811 fr_int2str(rlm_policy_tokens, compare, rhs));
812 rlm_policy_free_item((policy_item_t *) this);
816 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
817 if ((token != POLICY_LEX_BARE_WORD) &&
818 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
819 fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
820 lexer->filename, lexer->lineno);
821 rlm_policy_free_item((policy_item_t *) this);
824 debug_tokens("[COMPARE (%s %s %s)] ",
825 lhs, fr_int2str(rlm_policy_tokens, compare, "?"), rhs);
826 this->lhs = strdup(lhs);
827 this->compare = compare;
828 this->rhs_type = token;
829 this->rhs = strdup(rhs);
833 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
834 lexer->filename, lexer->lineno);
835 rlm_policy_free_item((policy_item_t *) this);
839 token = policy_lex_file(lexer, 0, NULL, 0);
840 if (token != POLICY_LEX_R_BRACKET) {
841 fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
842 lexer->filename, lexer->lineno,
843 fr_int2str(rlm_policy_tokens, token, "?"));
844 rlm_policy_free_item((policy_item_t *) this);
849 * After the end of condition, we MAY have && or ||
851 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
852 if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
853 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
854 debug_tokens("[%s] ",
855 fr_int2str(rlm_policy_tokens, token, "?"));
856 this->child_condition = token;
857 rcode = parse_condition(lexer, &(this->child));
859 rlm_policy_free_item((policy_item_t *) this);
864 *tail = (policy_item_t *) this;
872 * if (...) {...} else {...}
873 * if (...) {...} else if ...
875 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
882 debug_tokens("[IF] ");
884 this = rad_malloc(sizeof(*this));
885 memset(this, 0, sizeof(*this));
887 this->item.type = POLICY_TYPE_IF;
888 this->item.lineno = lexer->lineno;
890 rcode = parse_condition(lexer, &(this->condition));
892 rlm_policy_free_item((policy_item_t *) this);
896 rcode = parse_block(lexer, &(this->if_true));
898 rlm_policy_free_item((policy_item_t *) this);
902 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
903 mystring, sizeof(mystring));
904 if ((token == POLICY_LEX_BARE_WORD) &&
905 (fr_str2int(policy_reserved_words, mystring,
906 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
907 debug_tokens("[ELSE] ");
908 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
909 rad_assert(token == POLICY_LEX_BARE_WORD);
911 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
912 mystring, sizeof(mystring));
913 if ((token == POLICY_LEX_BARE_WORD) &&
914 (fr_str2int(policy_reserved_words, mystring,
915 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
916 token = policy_lex_file(lexer, 0,
917 mystring, sizeof(mystring));
918 rad_assert(token == POLICY_LEX_BARE_WORD);
919 rcode = parse_if(lexer, &(this->if_false));
921 rcode = parse_block(lexer, &(this->if_false));
924 rlm_policy_free_item((policy_item_t *) this);
932 * Empty "if" condition, don't even bother remembering
935 if (!this->if_true && !this->if_false) {
936 debug_tokens("Discarding empty \"if\" statement at line %d\n",
938 rlm_policy_free_item((policy_item_t *) this);
942 *tail = (policy_item_t *) this;
949 * Parse a reference to a named policy "foo()"
951 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
957 debug_tokens("[CALL] ");
959 token = policy_lex_file(lexer, 0, NULL, 0);
960 if (token != POLICY_LEX_L_BRACKET) {
961 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
962 lexer->filename, lexer->lineno,
963 fr_int2str(rlm_policy_tokens, token, "?"));
967 token = policy_lex_file(lexer, 0, NULL, 0);
968 if (token != POLICY_LEX_R_BRACKET) {
969 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
970 lexer->filename, lexer->lineno,
971 fr_int2str(rlm_policy_tokens, token, "?"));
975 this = rad_malloc(sizeof(*this));
976 memset(this, 0, sizeof(*this));
978 this->item.type = POLICY_TYPE_CALL;
979 this->item.lineno = lexer->lineno;
981 this->name = strdup(name);
983 *tail = (policy_item_t *) this;
990 * Edit/update/replace an attribute list
992 static int parse_attribute_block(policy_lex_file_t *lexer,
993 policy_item_t **tail,
994 policy_reserved_word_t where)
997 policy_attributes_t *this;
1000 this = rad_malloc(sizeof(*this));
1004 memset(this, 0, sizeof(*this));
1005 this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
1006 this->item.lineno = lexer->lineno;
1007 this->where = where;
1009 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1013 case POLICY_LEX_BEFORE_WHERE_EQUALS:
1014 case POLICY_LEX_AFTER_WHERE_EQUALS:
1015 case POLICY_LEX_BEFORE_WHERE_ASSIGN:
1016 case POLICY_LEX_AFTER_WHERE_ASSIGN:
1017 if (!parse_condition(lexer, &(this->where_loc))) {
1018 rlm_policy_free_item((policy_item_t *)this);
1022 case POLICY_LEX_BEFORE_HEAD_EQUALS:
1023 case POLICY_LEX_AFTER_TAIL_EQUALS:
1024 case POLICY_LEX_BEFORE_HEAD_ASSIGN:
1025 case POLICY_LEX_AFTER_TAIL_ASSIGN:
1026 case POLICY_LEX_ASSIGN:
1027 case POLICY_LEX_SET_EQUALS:
1028 case POLICY_LEX_CONCAT_EQUALS:
1032 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
1033 lexer->filename, lexer->lineno,
1034 fr_int2str(rlm_policy_tokens, token, "?"));
1035 return 0; /* unknown */
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(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");