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 LRAD_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 LRAD_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 LRAD_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_CONCAT_EQUALS },
114 { ":=", POLICY_LEX_SET_EQUALS },
115 { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
116 { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
117 { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
118 { "bare word", POLICY_LEX_BARE_WORD },
125 * Hand-coded lexical analysis of a string.
126 * Handed input string, updates token, possible a decoded
127 * string in buffer, and returns the pointer to the next token.
129 * Lexical tokens cannot cross a string boundary.
131 static const char *policy_lex_string(const char *input,
133 char *buffer, size_t buflen)
135 rad_assert(input != NULL);
137 if (buffer) *buffer = '\0';
141 *token = POLICY_LEX_EOL;
142 return NULL; /* nothing more to do */
149 * Skip over all of the whitespace in one swell foop.
151 *token = POLICY_LEX_WHITESPACE;
152 while ((*input == ' ') || (*input == '\t') ||
153 (*input == '\r') || (*input == '\n')) input++;
154 return input; /* point to next non-whitespace character */
156 case '#': /* ignore everything to the end of the line */
157 *token = POLICY_LEX_EOL;
161 *token = POLICY_LEX_L_BRACKET;
165 *token = POLICY_LEX_R_BRACKET;
169 *token = POLICY_LEX_LC_BRACKET;
173 *token = POLICY_LEX_RC_BRACKET;
177 *token = POLICY_LEX_COMMA;
183 *token = POLICY_LEX_PLUS_EQUALS;
188 *token = POLICY_LEX_PLUS;
196 *token = POLICY_LEX_MINUS_EQUALS;
201 *token = POLICY_LEX_MINUS;
207 if (input[1] == '=') {
208 *token = POLICY_LEX_CONCAT_EQUALS;
211 *token = POLICY_LEX_BAD;
215 if (input[1] == '=') {
216 *token = POLICY_LEX_SET_EQUALS;
219 *token = POLICY_LEX_BAD;
225 *token = POLICY_LEX_L_AND;
230 *token = POLICY_LEX_AND_EQUALS;
235 *token = POLICY_LEX_AND;
242 *token = POLICY_LEX_L_OR;
247 *token = POLICY_LEX_OR_EQUALS;
252 *token = POLICY_LEX_OR;
260 *token = POLICY_LEX_CMP_NOT_EQUALS;
265 *token = POLICY_LEX_RX_NOT_EQUALS;
270 *token = POLICY_LEX_CMP_FALSE;
274 *token = POLICY_LEX_L_NOT;
282 *token = POLICY_LEX_CMP_EQUALS;
287 *token = POLICY_LEX_RX_EQUALS;
292 *token = POLICY_LEX_CMP_TRUE;
296 *token = POLICY_LEX_ASSIGN;
301 if (input[1] == '=') {
303 *token = POLICY_LEX_LE;
305 *token = POLICY_LEX_LT;
310 if (input[1] == '=') {
312 *token = POLICY_LEX_GE;
314 *token = POLICY_LEX_GT;
320 *token = POLICY_LEX_BAD;
325 while (*input != '"') {
327 * Strings can't pass EOL.
330 return POLICY_LEX_BAD;
334 * FIXME: Embedded quotes?
336 *(buffer++) = *(input++);
340 * FIXME: Print more warnings?
348 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
349 return input + 1; /* skip trailing '"' */
351 default: /* bare word */
356 * It's a bare word, with nowhere to put it. Die.
359 *token = POLICY_LEX_BAD;
364 * Getting one character is stupid.
367 *token = POLICY_LEX_BAD;
372 * Bare words are [-a-zA-Z0-9.]+
375 if (!(((*input >= '0') && (*input <= '9')) ||
376 ((*input >= 'a') && (*input <= 'z')) ||
377 ((*input >= 'A') && (*input <= 'Z')) ||
378 (*input == '-') || (*input == '.') ||
379 (*input == ':') || (*input == '_'))) {
382 *(buffer++) = *(input++);
386 * FIXME: Print more warnings?
394 *token = POLICY_LEX_BARE_WORD;
400 * We want to lexically analyze a file, so we need a wrapper
401 * around the lexical analysis of strings.
403 typedef struct policy_lex_file_t {
406 const char *filename;
415 #define POLICY_LEX_FLAG_RETURN_EOL (1 << 0)
416 #define POLICY_LEX_FLAG_PEEK (1 << 1)
417 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
419 #define debug_tokens if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) printf
423 * Function to return a token saying what it read, and possibly
424 * a buffer of the quoted string or bare word.
426 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
428 char *mystring, size_t mystringlen)
430 policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
432 if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
433 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
437 return POLICY_LEX_EOF;
441 * Starting off, the buffer needs to be primed.
444 lexer->parse = fgets(lexer->buffer,
445 sizeof(lexer->buffer),
449 return POLICY_LEX_EOF;
453 } /* buffer is primed, read stuff */
455 if (lexer->token != POLICY_LEX_BAD) {
456 token = lexer->token;
457 lexer->token = POLICY_LEX_BAD;
462 * Ignore whitespace, and keep filling the buffer
464 while (lexer->parse) {
467 next = policy_lex_string(lexer->parse, &token,
468 mystring, mystringlen);
470 case POLICY_LEX_WHITESPACE: /* skip whitespace */
474 case POLICY_LEX_EOL: /* read another line */
475 lexer->parse = fgets(lexer->buffer,
476 sizeof(lexer->buffer),
479 if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
480 return POLICY_LEX_EOL;
482 break; /* read another token */
484 default: /* return the token */
485 if (!(flags & POLICY_LEX_FLAG_PEEK)) {
488 if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
489 debug_tokens("[%s token %s] ",
490 (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
491 lrad_int2str(rlm_policy_tokens,
497 } /* loop until EOF */
500 * Close it for the user.
505 return POLICY_LEX_EOF;
510 * Push a token back onto the input.
512 * FIXME: Push words, too?
514 static int policy_lex_push_token(policy_lex_file_t *lexer,
517 if (lexer->token != POLICY_LEX_BAD) {
522 lexer->token = token;
528 * Forward declarations.
530 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
534 * Map reserved words to tokens, and vice versa.
536 const LRAD_NAME_NUMBER policy_reserved_words[] = {
537 { "if", POLICY_RESERVED_IF },
538 { "else", POLICY_RESERVED_ELSE },
539 { "debug", POLICY_RESERVED_DEBUG },
540 { "print", POLICY_RESERVED_PRINT },
541 { "policy", POLICY_RESERVED_POLICY },
542 { "control", POLICY_RESERVED_CONTROL },
543 { "request", POLICY_RESERVED_REQUEST },
544 { "reply", POLICY_RESERVED_REPLY },
545 { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
546 { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
547 { "include", POLICY_RESERVED_INCLUDE },
548 { "return", POLICY_RESERVED_RETURN },
549 { "module", POLICY_RESERVED_MODULE },
550 { NULL, POLICY_RESERVED_UNKNOWN }
555 * Simplifies some later coding
557 static int policy_lex_str2int(policy_lex_file_t *lexer,
558 const LRAD_NAME_NUMBER *table, int default_value)
563 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
564 if (token != POLICY_LEX_BARE_WORD) {
565 fprintf(stderr, "%s[%d]: Unexpected token\n",
566 lexer->filename, lexer->lineno);
567 return default_value;
570 return lrad_str2int(table, buffer, default_value);
578 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
582 policy_print_t *this;
584 debug_tokens("[PRINT] ");
586 this = rad_malloc(sizeof(*this));
587 memset(this, 0, sizeof(*this));
589 this->item.type = POLICY_TYPE_PRINT;
590 this->item.lineno = lexer->lineno;
592 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
593 if ((token != POLICY_LEX_BARE_WORD) &&
594 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
595 fprintf(stderr, "%s[%d]: Bad print command\n",
596 lexer->filename, lexer->lineno);
600 this->rhs_type = token;
601 this->rhs = strdup(mystring);
603 *tail = (policy_item_t *) this;
610 * (foo == bar), with nested conditionals.
612 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
615 policy_lex_t token, compare;
616 char lhs[256], rhs[256];
617 policy_condition_t *this;
619 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
620 if (token != POLICY_LEX_L_BRACKET) {
621 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
622 lexer->filename, lexer->lineno,
623 lrad_int2str(rlm_policy_tokens, token, lhs));
627 this = rad_malloc(sizeof(*this));
628 memset(this, 0, sizeof(*this));
630 this->item.type = POLICY_TYPE_CONDITIONAL;
631 this->item.lineno = lexer->lineno;
633 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
635 case POLICY_LEX_L_BRACKET:
636 if (!policy_lex_push_token(lexer, token)) {
637 rlm_policy_free_item((policy_item_t *) this);
641 this->compare = POLICY_LEX_L_BRACKET;
642 this->child_condition = POLICY_LEX_L_BRACKET;
643 rcode = parse_condition(lexer, &(this->child));
645 rlm_policy_free_item((policy_item_t *) this);
650 case POLICY_LEX_L_NOT:
651 this->compare = POLICY_LEX_L_NOT;
652 debug_tokens("[NOT] ");
655 * FIXME: allow !foo, !foo=bar, etc.
657 * Maybe we should learn how to use lex && yacc?
660 rcode = parse_condition(lexer, &(this->child));
662 rlm_policy_free_item((policy_item_t *) this);
667 case POLICY_LEX_BARE_WORD:
668 this->lhs_type = token;
669 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
670 if (token == POLICY_LEX_L_BRACKET) {
671 debug_tokens("[IF-CALL %s] ", lhs);
676 if (rlm_policy_find(lexer->policies, lhs) == NULL) {
677 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
678 lexer->filename, lexer->lineno,
680 rlm_policy_free_item((policy_item_t *) this);
686 * this->lhs set up below, after "check"
688 this->lhs_type = POLICY_LEX_FUNCTION;
691 * Copied from parse_call
693 token = policy_lex_file(lexer, 0, NULL, 0);
694 if (token != POLICY_LEX_L_BRACKET) {
695 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
696 lexer->filename, lexer->lineno,
697 lrad_int2str(rlm_policy_tokens, token, "?"));
701 token = policy_lex_file(lexer, 0, NULL, 0);
702 if (token != POLICY_LEX_R_BRACKET) {
703 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
704 lexer->filename, lexer->lineno,
705 lrad_int2str(rlm_policy_tokens, token, "?"));
708 } /* else it's a comparison? */
711 case POLICY_LEX_DOUBLE_QUOTED_STRING:
712 this->lhs_type = token;
715 * Got word. May just be test for existence.
718 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
719 if (token == POLICY_LEX_R_BRACKET) {
720 debug_tokens("[TEST %s] ", lhs);
721 this->lhs = strdup(lhs);
722 this->compare = POLICY_LEX_CMP_TRUE;
726 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
728 case POLICY_LEX_CMP_EQUALS:
729 case POLICY_LEX_CMP_NOT_EQUALS:
730 case POLICY_LEX_RX_EQUALS:
731 case POLICY_LEX_RX_NOT_EQUALS:
732 case POLICY_LEX_CMP_TRUE:
733 case POLICY_LEX_CMP_FALSE:
741 fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
742 lexer->filename, lexer->lineno,
743 lrad_int2str(rlm_policy_tokens, compare, rhs));
744 rlm_policy_free_item((policy_item_t *) this);
748 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
749 if ((token != POLICY_LEX_BARE_WORD) &&
750 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
751 fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
752 lexer->filename, lexer->lineno);
753 rlm_policy_free_item((policy_item_t *) this);
756 debug_tokens("[COMPARE (%s %s %s)] ",
757 lhs, lrad_int2str(rlm_policy_tokens, compare, "?"), rhs);
758 this->lhs = strdup(lhs);
759 this->compare = compare;
760 this->rhs_type = token;
761 this->rhs = strdup(rhs);
765 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
766 lexer->filename, lexer->lineno);
767 rlm_policy_free_item((policy_item_t *) this);
771 token = policy_lex_file(lexer, 0, NULL, 0);
772 if (token != POLICY_LEX_R_BRACKET) {
773 fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
774 lexer->filename, lexer->lineno,
775 lrad_int2str(rlm_policy_tokens, token, "?"));
776 rlm_policy_free_item((policy_item_t *) this);
781 * After the end of condition, we MAY have && or ||
783 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
784 if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
785 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
786 debug_tokens("[%s] ",
787 lrad_int2str(rlm_policy_tokens, token, "?"));
788 this->child_condition = token;
789 rcode = parse_condition(lexer, &(this->child));
791 rlm_policy_free_item((policy_item_t *) this);
796 *tail = (policy_item_t *) this;
804 * if (...) {...} else {...}
805 * if (...) {...} else if ...
807 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
814 debug_tokens("[IF] ");
816 this = rad_malloc(sizeof(*this));
817 memset(this, 0, sizeof(*this));
819 this->item.type = POLICY_TYPE_IF;
820 this->item.lineno = lexer->lineno;
822 rcode = parse_condition(lexer, &(this->condition));
824 rlm_policy_free_item((policy_item_t *) this);
828 rcode = parse_block(lexer, &(this->if_true));
830 rlm_policy_free_item((policy_item_t *) this);
834 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
835 mystring, sizeof(mystring));
836 if ((token == POLICY_LEX_BARE_WORD) &&
837 (lrad_str2int(policy_reserved_words, mystring,
838 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
839 debug_tokens("[ELSE] ");
840 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
842 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
843 mystring, sizeof(mystring));
844 if ((token == POLICY_LEX_BARE_WORD) &&
845 (lrad_str2int(policy_reserved_words, mystring,
846 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
847 token = policy_lex_file(lexer, 0,
848 mystring, sizeof(mystring));
849 rcode = parse_if(lexer, &(this->if_false));
851 rcode = parse_block(lexer, &(this->if_false));
854 rlm_policy_free_item((policy_item_t *) this);
862 * Empty "if" condition, don't even bother remembering
865 if (!this->if_true && !this->if_false) {
866 debug_tokens("Discarding empty \"if\" statement at line %d\n",
868 rlm_policy_free_item((policy_item_t *) this);
872 *tail = (policy_item_t *) this;
879 * Parse a reference to a named policy "foo()"
881 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
887 debug_tokens("[CALL] ");
889 token = policy_lex_file(lexer, 0, NULL, 0);
890 if (token != POLICY_LEX_L_BRACKET) {
891 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
892 lexer->filename, lexer->lineno,
893 lrad_int2str(rlm_policy_tokens, token, "?"));
897 token = policy_lex_file(lexer, 0, NULL, 0);
898 if (token != POLICY_LEX_R_BRACKET) {
899 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
900 lexer->filename, lexer->lineno,
901 lrad_int2str(rlm_policy_tokens, token, "?"));
905 this = rad_malloc(sizeof(*this));
906 memset(this, 0, sizeof(*this));
908 this->item.type = POLICY_TYPE_CALL;
909 this->item.lineno = lexer->lineno;
911 this->name = strdup(name);
913 *tail = (policy_item_t *) this;
920 * Edit/update/replace an attribute list
922 static int parse_attribute_block(policy_lex_file_t *lexer,
923 policy_item_t **tail,
924 policy_reserved_word_t where)
927 policy_attributes_t *this;
930 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
932 case POLICY_LEX_ASSIGN:
933 case POLICY_LEX_SET_EQUALS:
934 case POLICY_LEX_CONCAT_EQUALS:
938 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
939 lexer->filename, lexer->lineno,
940 lrad_int2str(rlm_policy_tokens, token, "?"));
941 return 0; /* unknown */
944 this = rad_malloc(sizeof(*this));
945 memset(this, 0, sizeof(*this));
947 this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
948 this->item.lineno = lexer->lineno;
952 if (!parse_block(lexer, &(this->attributes))) {
953 rlm_policy_free_item((policy_item_t *) this);
957 *tail = (policy_item_t *) this;
963 * Parse a return statement.
965 static int parse_return(policy_lex_file_t *lexer, policy_item_t **tail)
969 policy_return_t *this;
971 rcode = policy_lex_str2int(lexer, policy_return_codes,
972 RLM_MODULE_NUMCODES);
973 if (rcode == RLM_MODULE_NUMCODES) {
974 fprintf(stderr, "%s[%d]: Invalid return code\n",
975 lexer->filename, lexer->lineno);
980 * Look for more sutff
982 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
984 if (token != POLICY_LEX_RC_BRACKET) {
985 fprintf(stderr, "%s[%d]: return statement must be the last statement in a policy.\n",
986 lexer->filename, lexer->lineno);
990 this = rad_malloc(sizeof(*this));
991 memset(this, 0, sizeof(*this));
993 this->item.type = POLICY_TYPE_RETURN;
994 this->item.lineno = lexer->lineno;
997 *tail = (policy_item_t *) this;
1003 const LRAD_NAME_NUMBER policy_component_names[] = {
1004 { "authenticate", RLM_COMPONENT_AUTH },
1005 { "authorize", RLM_COMPONENT_AUTZ },
1006 { "preacct", RLM_COMPONENT_PREACCT },
1007 { "accounting", RLM_COMPONENT_ACCT },
1008 { "session", RLM_COMPONENT_SESS },
1009 { "pre-proxy", RLM_COMPONENT_PRE_PROXY },
1010 { "post-proxy", RLM_COMPONENT_POST_PROXY },
1011 { "post-auth", RLM_COMPONENT_POST_AUTH },
1012 { NULL, RLM_COMPONENT_COUNT }
1016 * Parse a module statement.
1018 static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
1022 policy_module_t *this;
1024 const char *section_name;
1025 char filename[1024];
1027 CONF_SECTION *cs, *subcs;
1033 token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1034 if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1035 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1036 lexer->filename, lexer->lineno,
1037 lrad_int2str(rlm_policy_tokens, token, "?"));
1042 * See if we're including all of the files in a subdirectory.
1044 strNcpy(buffer, lexer->filename, sizeof(buffer));
1045 p = strrchr(buffer, '/');
1047 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1049 snprintf(buffer, sizeof(buffer), "%s/%s",
1050 radius_dir, filename);
1054 * Include section calling a module.
1056 debug_tokens("including module section from file %s\n", buffer);
1057 cs = cf_file_read(buffer);
1059 return 0; /* it prints out error messages */
1063 * The outer section is called "main", and can be ignored.
1064 * It should be a section, so there should be a subsection.
1066 subcs = cf_subsection_find_next(cs, NULL, NULL);
1068 fprintf(stderr, "%s[%d]: Expected section containing modules\n",
1069 lexer->filename, lexer->lineno);
1070 cf_section_free(&cs);
1074 section_name = cf_section_name1(subcs);
1075 rad_assert(section_name != NULL);
1076 component = lrad_str2int(policy_component_names, section_name,
1077 RLM_COMPONENT_COUNT);
1078 if (component == RLM_COMPONENT_COUNT) {
1079 fprintf(stderr, "%s[%d]: Invalid section name \"%s\"\n",
1080 lexer->filename, lexer->lineno, section_name);
1081 cf_section_free(&cs);
1086 * Compile the module entry.
1088 mc = compile_modgroup(component, subcs, buffer);
1090 cf_section_free(&cs);
1091 return 0; /* more often results in calling exit... */
1094 this = rad_malloc(sizeof(*this));
1095 memset(this, 0, sizeof(*this));
1097 this->item.type = POLICY_TYPE_MODULE;
1098 this->item.lineno = lexer->lineno;
1099 this->component = component;
1103 *tail = (policy_item_t *) this;
1110 * Parse one statement. 'foo = bar', or 'if (...) {...}', or '{...}',
1113 static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
1116 policy_reserved_word_t reserved;
1117 policy_lex_t token, assign;
1118 char lhs[256], rhs[256];
1119 policy_assignment_t *this;
1122 * See what kind of token we have.
1124 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
1126 case POLICY_LEX_LC_BRACKET:
1127 rcode = parse_block(lexer, tail);
1133 case POLICY_LEX_BARE_WORD:
1134 reserved = lrad_str2int(policy_reserved_words,
1136 POLICY_RESERVED_UNKNOWN);
1138 case POLICY_RESERVED_IF:
1139 if (parse_if(lexer, tail)) {
1145 case POLICY_RESERVED_CONTROL:
1146 case POLICY_RESERVED_REQUEST:
1147 case POLICY_RESERVED_REPLY:
1148 case POLICY_RESERVED_PROXY_REQUEST:
1149 case POLICY_RESERVED_PROXY_REPLY:
1150 if (parse_attribute_block(lexer, tail,
1156 case POLICY_RESERVED_PRINT:
1157 if (parse_print(lexer, tail)) {
1163 case POLICY_RESERVED_RETURN:
1164 if (parse_return(lexer, tail)) {
1170 case POLICY_RESERVED_MODULE:
1171 if (parse_module(lexer, tail)) {
1177 case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
1179 * Is a named policy, parse the reference to it.
1181 if (rlm_policy_find(lexer->policies, lhs) != NULL) {
1182 if (!parse_call(lexer, tail, lhs)) {
1189 const DICT_ATTR *dattr;
1192 * Bare words MUST be dictionary attributes
1195 dattr = dict_attrbyname(lhs);
1197 fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
1198 lexer->filename, lexer->lineno, lhs);
1201 debug_tokens("%s[%d]: Got attribute %s\n",
1202 lexer->filename, lexer->lineno,
1208 fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
1209 lexer->filename, lexer->lineno, lhs);
1211 } /* switch over reserved words */
1215 * Return from nested blocks.
1217 case POLICY_LEX_RC_BRACKET:
1218 policy_lex_push_token(lexer, token);
1219 return 2; /* magic */
1221 case POLICY_LEX_EOF: /* nothing more to do */
1225 fprintf(stderr, "%s[%d]: Unexpected %s\n",
1226 lexer->filename, lexer->lineno,
1227 lrad_int2str(policy_explanations,
1233 * Parse a bare statement.
1235 assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1237 case POLICY_LEX_ASSIGN:
1238 case POLICY_LEX_SET_EQUALS:
1239 case POLICY_LEX_AND_EQUALS:
1240 case POLICY_LEX_OR_EQUALS:
1241 case POLICY_LEX_PLUS_EQUALS:
1245 fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
1246 lexer->filename, lexer->lineno,
1247 lrad_int2str(policy_explanations,
1252 this = rad_malloc(sizeof(*this));
1253 memset(this, 0, sizeof(*this));
1255 this->item.type = POLICY_TYPE_ASSIGNMENT;
1256 this->item.lineno = lexer->lineno;
1258 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1259 if ((token != POLICY_LEX_BARE_WORD) &&
1260 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
1261 fprintf(stderr, "%s[%d]: Unexpected rhs %s\n",
1262 lexer->filename, lexer->lineno,
1263 lrad_int2str(policy_explanations,
1265 rlm_policy_free_item((policy_item_t *) this);
1268 this->rhs_type = token;
1269 this->rhs = strdup(rhs);
1271 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1273 if (token != POLICY_LEX_EOL) {
1274 fprintf(stderr, "%s[%d]: Expected EOL\n",
1275 lexer->filename, lexer->lineno);
1276 rlm_policy_free_item((policy_item_t *) this);
1279 debug_tokens("[ASSIGN %s %s %s]\n",
1280 lhs, lrad_int2str(rlm_policy_tokens, assign, "?"), rhs);
1283 * Fill in the assignment struct
1285 this->lhs = strdup(lhs);
1286 this->assign = assign;
1288 *tail = (policy_item_t *) this;
1295 * Parse block of statements. The block has already been checked
1296 * to begin with a '{'.
1298 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail)
1303 debug_tokens("[BLOCK] ");
1305 token = policy_lex_file(lexer, 0, NULL, 0);
1306 if (token != POLICY_LEX_LC_BRACKET) {
1307 fprintf(stderr, "%s[%d]: Expected '{'\n",
1308 lexer->filename, lexer->lineno);
1313 while ((rcode = parse_statement(lexer, tail)) != 0) {
1315 token = policy_lex_file(lexer, 0, NULL, 0);
1316 if (token != POLICY_LEX_RC_BRACKET) {
1317 fprintf(stderr, "%s[%d]: Expected '}'\n",
1318 lexer->filename, lexer->lineno);
1323 rad_assert(*tail != NULL);
1324 /* parse_statement must fill this in */
1325 while (*tail) tail = &((*tail)->next);
1330 * Parse statement failed.
1337 * Parse debugging statements
1339 static int parse_debug(policy_lex_file_t *lexer)
1345 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1346 if (token != POLICY_LEX_BARE_WORD) {
1347 fprintf(stderr, "%s[%d]: Bad debug command\n",
1348 lexer->filename, lexer->lineno);
1352 if (strcasecmp(buffer, "none") == 0) {
1353 lexer->debug = POLICY_DEBUG_NONE;
1356 } else if (strcasecmp(buffer, "peek") == 0) {
1357 lexer->debug |= POLICY_DEBUG_PEEK;
1360 } else if (strcasecmp(buffer, "print_tokens") == 0) {
1361 lexer->debug |= POLICY_DEBUG_PRINT_TOKENS;
1364 } else if (strcasecmp(buffer, "print_policy") == 0) {
1365 lexer->debug |= POLICY_DEBUG_PRINT_POLICY;
1368 } else if (strcasecmp(buffer, "evaluate") == 0) {
1369 lexer->debug |= POLICY_DEBUG_EVALUATE;
1374 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1376 if (token != POLICY_LEX_EOL) {
1377 fprintf(stderr, "%s[%d]: Expected EOL\n",
1378 lexer->filename, lexer->lineno);
1382 fprintf(stderr, "%s[%d]: Bad debug command \"%s\"\n",
1383 lexer->filename, lexer->lineno, buffer);
1392 * Parse a named policy "policy foo {...}"
1394 static int parse_named_policy(policy_lex_file_t *lexer)
1399 policy_named_t *this;
1402 debug_tokens("[POLICY] ");
1404 this = rad_malloc(sizeof(*this));
1405 memset(this, 0, sizeof(*this));
1407 this->item.type = POLICY_TYPE_NAMED_POLICY;
1408 this->item.lineno = lexer->lineno;
1410 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
1411 if (token != POLICY_LEX_BARE_WORD) {
1412 fprintf(stderr, "%s[%d]: Expected policy name, got \"%s\"\n",
1413 lexer->filename, lexer->lineno,
1414 lrad_int2str(rlm_policy_tokens, token, "?"));
1415 rlm_policy_free_item((policy_item_t *) this);
1419 dattr = dict_attrbyname(mystring);
1421 fprintf(stderr, "%s[%d]: Invalid policy name \"%s\": it is already defined as a dictionary attribute\n",
1422 lexer->filename, lexer->lineno, mystring);
1423 rlm_policy_free_item((policy_item_t *) this);
1427 this->name = strdup(mystring);
1428 rcode = parse_block(lexer, &(this->policy));
1430 rlm_policy_free_item((policy_item_t *) this);
1435 * And insert it into the tree of policies.
1437 * For now, policy names aren't scoped, they're global.
1439 if (!rlm_policy_insert(lexer->policies, this)) {
1440 fprintf(stderr, "Failed to insert policy \"%s\"\n", this->name);
1441 rlm_policy_free_item((policy_item_t *) this);
1446 * Do NOT add it into the list of parsed expressions!
1447 * The above insertion will take care of freeing it if
1448 * anything goes wrong...
1455 * Parse an "include filename" statement
1457 * FIXME: Tie this file into the CONF_SECTION for HUP handling!
1459 static int parse_include(policy_lex_file_t *lexer)
1463 char filename[1024];
1466 token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1467 if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1468 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1469 lexer->filename, lexer->lineno,
1470 lrad_int2str(rlm_policy_tokens, token, "?"));
1475 * See if we're including all of the files in a subdirectory.
1477 strNcpy(buffer, lexer->filename, sizeof(buffer));
1478 p = strrchr(buffer, '/');
1480 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1482 #ifdef HAVE_DIRENT_H
1483 p = strrchr(p + 1, '/');
1490 dir = opendir(buffer);
1492 fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
1493 lexer->filename, lexer->lineno,
1494 buffer, strerror(errno));
1499 * Read the directory, ignoring "." files.
1501 while ((dp = readdir(dir)) != NULL) {
1504 if (dp->d_name[0] == '.') continue;
1505 if (strchr(dp->d_name, '~') != NULL) continue;
1507 strNcpy(p, dp->d_name,
1508 sizeof(buffer) - (p - buffer));
1510 if ((stat(buffer, &buf) != 0) ||
1511 S_ISDIR(buf.st_mode)) continue;
1513 debug_tokens("\nincluding file %s\n", buffer);
1514 if (!rlm_policy_parse(lexer->policies, buffer)) {
1521 } /* else it must have been a normalx file */
1524 snprintf(buffer, sizeof(buffer), "%s/%s",
1525 radius_dir, filename);
1529 * Handle one include file.
1531 debug_tokens("\nincluding file %s\n", buffer);
1532 if (!rlm_policy_parse(lexer->policies, buffer)) {
1541 * Parse data from a file into a policy language.
1543 int rlm_policy_parse(rbtree_t *policies, const char *filename)
1547 policy_lex_file_t mylexer, *lexer = NULL;
1550 fp = fopen(filename, "r");
1552 fprintf(stderr, "Failed to open %s: %s\n",
1553 filename, strerror(errno));
1558 memset(lexer, 0, sizeof(*lexer));
1559 lexer->filename = filename;
1561 lexer->token = POLICY_LEX_BAD;
1562 lexer->parse = NULL; /* initial input */
1563 lexer->policies = policies;
1568 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1570 case POLICY_LEX_BARE_WORD:
1571 reserved = lrad_str2int(policy_reserved_words,
1573 POLICY_RESERVED_UNKNOWN);
1575 case POLICY_RESERVED_POLICY:
1576 if (!parse_named_policy(lexer)) {
1581 case POLICY_RESERVED_INCLUDE:
1582 if (!parse_include(lexer)) {
1587 case POLICY_RESERVED_DEBUG:
1588 if (!parse_debug(lexer)) {
1594 fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
1595 lexer->filename, lexer->lineno,
1599 } /* switch over reserved words */
1601 case POLICY_LEX_EOF:
1605 fprintf(stderr, "%s[%d]: Illegal input\n",
1606 lexer->filename, lexer->lineno);
1611 } while (token != POLICY_LEX_EOF);
1614 debug_tokens("--------------------------------------------------\n");