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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2004 Alan DeKok <aland@ox.org>
23 #include "rlm_policy.h"
28 #ifdef HAVE_SYS_STAT_H
33 #include <freeradius-devel/modules.h>
35 const LRAD_NAME_NUMBER policy_return_codes[] = {
36 { "reject", RLM_MODULE_REJECT },
37 { "fail", RLM_MODULE_FAIL },
38 { "ok", RLM_MODULE_OK },
39 { "handled", RLM_MODULE_HANDLED },
40 { "invalid", RLM_MODULE_INVALID },
41 { "userlock", RLM_MODULE_USERLOCK },
42 { "notfound", RLM_MODULE_NOTFOUND },
43 { "noop", RLM_MODULE_NOOP },
44 { "updated", RLM_MODULE_UPDATED },
45 { NULL, RLM_MODULE_NUMCODES }
49 * Explanations of what the lexical tokens are.
51 static const LRAD_NAME_NUMBER policy_explanations[] = {
52 { "invalid input", POLICY_LEX_BAD },
53 { "end of file", POLICY_LEX_EOF },
54 { "end of line", POLICY_LEX_EOL },
55 { "whitespace", POLICY_LEX_WHITESPACE },
56 { "hash mark", POLICY_LEX_HASH },
57 { "left bracket", POLICY_LEX_L_BRACKET },
58 { "right bracket", POLICY_LEX_R_BRACKET },
59 { "{", POLICY_LEX_LC_BRACKET },
60 { "}", POLICY_LEX_RC_BRACKET },
61 { "comma", POLICY_LEX_COMMA },
62 { "logical AND", POLICY_LEX_L_AND },
63 { "logical OR", POLICY_LEX_L_OR },
64 { "AND", POLICY_LEX_AND },
65 { "OR", POLICY_LEX_OR },
66 { "logical NOT", POLICY_LEX_L_NOT },
67 { "assignment", POLICY_LEX_ASSIGN },
68 { "comparison", POLICY_LEX_CMP_EQUALS },
69 { "comparison", POLICY_LEX_CMP_NOT_EQUALS },
70 { "comparison", POLICY_LEX_LT },
71 { "comparison", POLICY_LEX_GT },
72 { "comparison", POLICY_LEX_LE },
73 { "comparison", POLICY_LEX_GT },
74 { "comparison", POLICY_LEX_RX_EQUALS },
75 { "comparison", POLICY_LEX_RX_NOT_EQUALS },
76 { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
77 { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
78 { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
79 { "bare word", POLICY_LEX_BARE_WORD },
85 const LRAD_NAME_NUMBER rlm_policy_tokens[] = {
86 { "EOF", POLICY_LEX_EOF },
87 { "#", POLICY_LEX_HASH },
88 { "(", POLICY_LEX_L_BRACKET },
89 { ")", POLICY_LEX_R_BRACKET },
90 { "{", POLICY_LEX_LC_BRACKET },
91 { "}", POLICY_LEX_RC_BRACKET },
92 { ",", POLICY_LEX_COMMA },
93 { "&&", POLICY_LEX_L_AND },
94 { "||", POLICY_LEX_L_OR },
95 { "&", POLICY_LEX_AND },
96 { "|", POLICY_LEX_OR },
97 { "!", POLICY_LEX_L_NOT },
98 { "=", POLICY_LEX_ASSIGN },
99 { "==", POLICY_LEX_CMP_EQUALS },
100 { "!=", POLICY_LEX_CMP_NOT_EQUALS },
101 { "=*", POLICY_LEX_CMP_TRUE },
102 { "!*", POLICY_LEX_CMP_FALSE },
103 { "<", POLICY_LEX_LT },
104 { ">", POLICY_LEX_GT },
105 { "<=", POLICY_LEX_LE },
106 { ">=", POLICY_LEX_GT },
107 { "=~", POLICY_LEX_RX_EQUALS },
108 { "!~", POLICY_LEX_RX_NOT_EQUALS },
109 { ".=", POLICY_LEX_CONCAT_EQUALS },
110 { ":=", POLICY_LEX_SET_EQUALS },
111 { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
112 { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
113 { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
114 { "bare word", POLICY_LEX_BARE_WORD },
121 * Hand-coded lexical analysis of a string.
122 * Handed input string, updates token, possible a decoded
123 * string in buffer, and returns the pointer to the next token.
125 * Lexical tokens cannot cross a string boundary.
127 static const char *policy_lex_string(const char *input,
129 char *buffer, size_t buflen)
131 rad_assert(input != NULL);
133 if (buffer) *buffer = '\0';
137 *token = POLICY_LEX_EOL;
138 return NULL; /* nothing more to do */
145 * Skip over all of the whitespace in one swell foop.
147 *token = POLICY_LEX_WHITESPACE;
148 while ((*input == ' ') || (*input == '\t') ||
149 (*input == '\r') || (*input == '\n')) input++;
150 return input; /* point to next non-whitespace character */
152 case '#': /* ignore everything to the end of the line */
153 *token = POLICY_LEX_EOL;
157 *token = POLICY_LEX_L_BRACKET;
161 *token = POLICY_LEX_R_BRACKET;
165 *token = POLICY_LEX_LC_BRACKET;
169 *token = POLICY_LEX_RC_BRACKET;
173 *token = POLICY_LEX_COMMA;
179 *token = POLICY_LEX_PLUS_EQUALS;
184 *token = POLICY_LEX_PLUS;
192 *token = POLICY_LEX_MINUS_EQUALS;
197 *token = POLICY_LEX_MINUS;
203 if (input[1] == '=') {
204 *token = POLICY_LEX_CONCAT_EQUALS;
207 *token = POLICY_LEX_BAD;
211 if (input[1] == '=') {
212 *token = POLICY_LEX_SET_EQUALS;
215 *token = POLICY_LEX_BAD;
221 *token = POLICY_LEX_L_AND;
226 *token = POLICY_LEX_AND_EQUALS;
231 *token = POLICY_LEX_AND;
238 *token = POLICY_LEX_L_OR;
243 *token = POLICY_LEX_OR_EQUALS;
248 *token = POLICY_LEX_OR;
256 *token = POLICY_LEX_CMP_NOT_EQUALS;
261 *token = POLICY_LEX_RX_NOT_EQUALS;
266 *token = POLICY_LEX_CMP_FALSE;
270 *token = POLICY_LEX_L_NOT;
278 *token = POLICY_LEX_CMP_EQUALS;
283 *token = POLICY_LEX_RX_EQUALS;
288 *token = POLICY_LEX_CMP_TRUE;
292 *token = POLICY_LEX_ASSIGN;
297 if (input[1] == '=') {
299 *token = POLICY_LEX_LE;
301 *token = POLICY_LEX_LT;
306 if (input[1] == '=') {
308 *token = POLICY_LEX_GE;
310 *token = POLICY_LEX_GT;
316 *token = POLICY_LEX_BAD;
321 while (*input != '"') {
323 * Strings can't pass EOL.
326 return POLICY_LEX_BAD;
330 * FIXME: Embedded quotes?
332 *(buffer++) = *(input++);
336 * FIXME: Print more warnings?
344 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
345 return input + 1; /* skip trailing '"' */
347 default: /* bare word */
352 * It's a bare word, with nowhere to put it. Die.
355 *token = POLICY_LEX_BAD;
360 * Getting one character is stupid.
363 *token = POLICY_LEX_BAD;
368 * Bare words are [-a-zA-Z0-9.]+
371 if (!(((*input >= '0') && (*input <= '9')) ||
372 ((*input >= 'a') && (*input <= 'z')) ||
373 ((*input >= 'A') && (*input <= 'Z')) ||
374 (*input == '-') || (*input == '.') ||
375 (*input == ':') || (*input == '_'))) {
378 *(buffer++) = *(input++);
382 * FIXME: Print more warnings?
390 *token = POLICY_LEX_BARE_WORD;
396 * We want to lexically analyze a file, so we need a wrapper
397 * around the lexical analysis of strings.
399 typedef struct policy_lex_file_t {
402 const char *filename;
411 #define POLICY_LEX_FLAG_RETURN_EOL (1 << 0)
412 #define POLICY_LEX_FLAG_PEEK (1 << 1)
413 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
415 #define debug_tokens if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) printf
419 * Function to return a token saying what it read, and possibly
420 * a buffer of the quoted string or bare word.
422 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
424 char *mystring, size_t mystringlen)
426 policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
428 if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
429 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
433 return POLICY_LEX_EOF;
437 * Starting off, the buffer needs to be primed.
440 lexer->parse = fgets(lexer->buffer,
441 sizeof(lexer->buffer),
445 return POLICY_LEX_EOF;
449 } /* buffer is primed, read stuff */
451 if (lexer->token != POLICY_LEX_BAD) {
452 token = lexer->token;
453 lexer->token = POLICY_LEX_BAD;
458 * Ignore whitespace, and keep filling the buffer
460 while (lexer->parse) {
463 next = policy_lex_string(lexer->parse, &token,
464 mystring, mystringlen);
466 case POLICY_LEX_WHITESPACE: /* skip whitespace */
470 case POLICY_LEX_EOL: /* read another line */
471 lexer->parse = fgets(lexer->buffer,
472 sizeof(lexer->buffer),
475 if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
476 return POLICY_LEX_EOL;
478 break; /* read another token */
480 default: /* return the token */
481 if (!(flags & POLICY_LEX_FLAG_PEEK)) {
484 if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
485 debug_tokens("[%s token %s] ",
486 (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
487 lrad_int2str(rlm_policy_tokens,
493 } /* loop until EOF */
496 * Close it for the user.
501 return POLICY_LEX_EOF;
506 * Push a token back onto the input.
508 * FIXME: Push words, too?
510 static int policy_lex_push_token(policy_lex_file_t *lexer,
513 if (lexer->token != POLICY_LEX_BAD) {
518 lexer->token = token;
524 * Forward declarations.
526 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
530 * Map reserved words to tokens, and vice versa.
532 const LRAD_NAME_NUMBER policy_reserved_words[] = {
533 { "if", POLICY_RESERVED_IF },
534 { "else", POLICY_RESERVED_ELSE },
535 { "debug", POLICY_RESERVED_DEBUG },
536 { "print", POLICY_RESERVED_PRINT },
537 { "policy", POLICY_RESERVED_POLICY },
538 { "control", POLICY_RESERVED_CONTROL },
539 { "request", POLICY_RESERVED_REQUEST },
540 { "reply", POLICY_RESERVED_REPLY },
541 { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
542 { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
543 { "include", POLICY_RESERVED_INCLUDE },
544 { "return", POLICY_RESERVED_RETURN },
545 { "module", POLICY_RESERVED_MODULE },
546 { NULL, POLICY_RESERVED_UNKNOWN }
551 * Simplifies some later coding
553 static int policy_lex_str2int(policy_lex_file_t *lexer,
554 const LRAD_NAME_NUMBER *table, int default_value)
559 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
560 if (token != POLICY_LEX_BARE_WORD) {
561 fprintf(stderr, "%s[%d]: Unexpected token\n",
562 lexer->filename, lexer->lineno);
563 return default_value;
566 return lrad_str2int(table, buffer, default_value);
574 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
578 policy_print_t *this;
580 debug_tokens("[PRINT] ");
582 this = rad_malloc(sizeof(*this));
583 memset(this, 0, sizeof(*this));
585 this->item.type = POLICY_TYPE_PRINT;
586 this->item.lineno = lexer->lineno;
588 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
589 if ((token != POLICY_LEX_BARE_WORD) &&
590 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
591 fprintf(stderr, "%s[%d]: Bad print command\n",
592 lexer->filename, lexer->lineno);
596 this->rhs_type = token;
597 this->rhs = strdup(mystring);
599 *tail = (policy_item_t *) this;
606 * (foo == bar), with nested conditionals.
608 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
611 policy_lex_t token, compare;
612 char lhs[256], rhs[256];
613 policy_condition_t *this;
615 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
616 if (token != POLICY_LEX_L_BRACKET) {
617 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
618 lexer->filename, lexer->lineno,
619 lrad_int2str(rlm_policy_tokens, token, lhs));
623 this = rad_malloc(sizeof(*this));
624 memset(this, 0, sizeof(*this));
626 this->item.type = POLICY_TYPE_CONDITIONAL;
627 this->item.lineno = lexer->lineno;
629 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
631 case POLICY_LEX_L_BRACKET:
632 if (!policy_lex_push_token(lexer, token)) {
633 rlm_policy_free_item((policy_item_t *) this);
637 this->compare = POLICY_LEX_L_BRACKET;
638 this->child_condition = POLICY_LEX_L_BRACKET;
639 rcode = parse_condition(lexer, &(this->child));
641 rlm_policy_free_item((policy_item_t *) this);
646 case POLICY_LEX_L_NOT:
647 this->compare = POLICY_LEX_L_NOT;
648 debug_tokens("[NOT] ");
651 * FIXME: allow !foo, !foo=bar, etc.
653 * Maybe we should learn how to use lex && yacc?
656 rcode = parse_condition(lexer, &(this->child));
658 rlm_policy_free_item((policy_item_t *) this);
663 case POLICY_LEX_BARE_WORD:
664 this->lhs_type = token;
665 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
666 if (token == POLICY_LEX_L_BRACKET) {
667 debug_tokens("[IF-CALL %s] ", lhs);
672 if (rlm_policy_find(lexer->policies, lhs) == NULL) {
673 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
674 lexer->filename, lexer->lineno,
676 rlm_policy_free_item((policy_item_t *) this);
682 * this->lhs set up below, after "check"
684 this->lhs_type = POLICY_LEX_FUNCTION;
687 * Copied from parse_call
689 token = policy_lex_file(lexer, 0, NULL, 0);
690 if (token != POLICY_LEX_L_BRACKET) {
691 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
692 lexer->filename, lexer->lineno,
693 lrad_int2str(rlm_policy_tokens, token, "?"));
697 token = policy_lex_file(lexer, 0, NULL, 0);
698 if (token != POLICY_LEX_R_BRACKET) {
699 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
700 lexer->filename, lexer->lineno,
701 lrad_int2str(rlm_policy_tokens, token, "?"));
704 } /* else it's a comparison? */
707 case POLICY_LEX_DOUBLE_QUOTED_STRING:
708 this->lhs_type = token;
711 * Got word. May just be test for existence.
714 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
715 if (token == POLICY_LEX_R_BRACKET) {
716 debug_tokens("[TEST %s] ", lhs);
717 this->lhs = strdup(lhs);
718 this->compare = POLICY_LEX_CMP_TRUE;
722 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
724 case POLICY_LEX_CMP_EQUALS:
725 case POLICY_LEX_CMP_NOT_EQUALS:
726 case POLICY_LEX_RX_EQUALS:
727 case POLICY_LEX_RX_NOT_EQUALS:
728 case POLICY_LEX_CMP_TRUE:
729 case POLICY_LEX_CMP_FALSE:
737 fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
738 lexer->filename, lexer->lineno,
739 lrad_int2str(rlm_policy_tokens, compare, rhs));
740 rlm_policy_free_item((policy_item_t *) this);
744 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
745 if ((token != POLICY_LEX_BARE_WORD) &&
746 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
747 fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
748 lexer->filename, lexer->lineno);
749 rlm_policy_free_item((policy_item_t *) this);
752 debug_tokens("[COMPARE (%s %s %s)] ",
753 lhs, lrad_int2str(rlm_policy_tokens, compare, "?"), rhs);
754 this->lhs = strdup(lhs);
755 this->compare = compare;
756 this->rhs_type = token;
757 this->rhs = strdup(rhs);
761 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
762 lexer->filename, lexer->lineno);
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 ')', got \"%s\"\n",
770 lexer->filename, lexer->lineno,
771 lrad_int2str(rlm_policy_tokens, token, "?"));
772 rlm_policy_free_item((policy_item_t *) this);
777 * After the end of condition, we MAY have && or ||
779 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
780 if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
781 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
782 debug_tokens("[%s] ",
783 lrad_int2str(rlm_policy_tokens, token, "?"));
784 this->child_condition = token;
785 rcode = parse_condition(lexer, &(this->child));
787 rlm_policy_free_item((policy_item_t *) this);
792 *tail = (policy_item_t *) this;
800 * if (...) {...} else {...}
801 * if (...) {...} else if ...
803 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
810 debug_tokens("[IF] ");
812 this = rad_malloc(sizeof(*this));
813 memset(this, 0, sizeof(*this));
815 this->item.type = POLICY_TYPE_IF;
816 this->item.lineno = lexer->lineno;
818 rcode = parse_condition(lexer, &(this->condition));
820 rlm_policy_free_item((policy_item_t *) this);
824 rcode = parse_block(lexer, &(this->if_true));
826 rlm_policy_free_item((policy_item_t *) this);
830 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
831 mystring, sizeof(mystring));
832 if ((token == POLICY_LEX_BARE_WORD) &&
833 (lrad_str2int(policy_reserved_words, mystring,
834 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
835 debug_tokens("[ELSE] ");
836 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
838 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
839 mystring, sizeof(mystring));
840 if ((token == POLICY_LEX_BARE_WORD) &&
841 (lrad_str2int(policy_reserved_words, mystring,
842 POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
843 token = policy_lex_file(lexer, 0,
844 mystring, sizeof(mystring));
845 rcode = parse_if(lexer, &(this->if_false));
847 rcode = parse_block(lexer, &(this->if_false));
850 rlm_policy_free_item((policy_item_t *) this);
858 * Empty "if" condition, don't even bother remembering
861 if (!this->if_true && !this->if_false) {
862 debug_tokens("Discarding empty \"if\" statement at line %d\n",
864 rlm_policy_free_item((policy_item_t *) this);
868 *tail = (policy_item_t *) this;
875 * Parse a reference to a named policy "foo()"
877 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
883 debug_tokens("[CALL] ");
885 token = policy_lex_file(lexer, 0, NULL, 0);
886 if (token != POLICY_LEX_L_BRACKET) {
887 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
888 lexer->filename, lexer->lineno,
889 lrad_int2str(rlm_policy_tokens, token, "?"));
893 token = policy_lex_file(lexer, 0, NULL, 0);
894 if (token != POLICY_LEX_R_BRACKET) {
895 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
896 lexer->filename, lexer->lineno,
897 lrad_int2str(rlm_policy_tokens, token, "?"));
901 this = rad_malloc(sizeof(*this));
902 memset(this, 0, sizeof(*this));
904 this->item.type = POLICY_TYPE_CALL;
905 this->item.lineno = lexer->lineno;
907 this->name = strdup(name);
909 *tail = (policy_item_t *) this;
916 * Edit/update/replace an attribute list
918 static int parse_attribute_block(policy_lex_file_t *lexer,
919 policy_item_t **tail,
920 policy_reserved_word_t where)
923 policy_attributes_t *this;
926 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
928 case POLICY_LEX_ASSIGN:
929 case POLICY_LEX_SET_EQUALS:
930 case POLICY_LEX_CONCAT_EQUALS:
934 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
935 lexer->filename, lexer->lineno,
936 lrad_int2str(rlm_policy_tokens, token, "?"));
937 return 0; /* unknown */
940 this = rad_malloc(sizeof(*this));
941 memset(this, 0, sizeof(*this));
943 this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
944 this->item.lineno = lexer->lineno;
948 if (!parse_block(lexer, &(this->attributes))) {
949 rlm_policy_free_item((policy_item_t *) this);
953 *tail = (policy_item_t *) this;
959 * Parse a return statement.
961 static int parse_return(policy_lex_file_t *lexer, policy_item_t **tail)
965 policy_return_t *this;
967 rcode = policy_lex_str2int(lexer, policy_return_codes,
968 RLM_MODULE_NUMCODES);
969 if (rcode == RLM_MODULE_NUMCODES) {
970 fprintf(stderr, "%s[%d]: Invalid return code\n",
971 lexer->filename, lexer->lineno);
976 * Look for more sutff
978 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
980 if (token != POLICY_LEX_RC_BRACKET) {
981 fprintf(stderr, "%s[%d]: return statement must be the last statement in a policy.\n",
982 lexer->filename, lexer->lineno);
986 this = rad_malloc(sizeof(*this));
987 memset(this, 0, sizeof(*this));
989 this->item.type = POLICY_TYPE_RETURN;
990 this->item.lineno = lexer->lineno;
993 *tail = (policy_item_t *) this;
999 const LRAD_NAME_NUMBER policy_component_names[] = {
1000 { "authenticate", RLM_COMPONENT_AUTH },
1001 { "authorize", RLM_COMPONENT_AUTZ },
1002 { "preacct", RLM_COMPONENT_PREACCT },
1003 { "accounting", RLM_COMPONENT_ACCT },
1004 { "session", RLM_COMPONENT_SESS },
1005 { "pre-proxy", RLM_COMPONENT_PRE_PROXY },
1006 { "post-proxy", RLM_COMPONENT_POST_PROXY },
1007 { "post-auth", RLM_COMPONENT_POST_AUTH },
1008 { NULL, RLM_COMPONENT_COUNT }
1012 * Parse a module statement.
1014 static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
1018 policy_module_t *this;
1020 const char *section_name;
1021 char filename[1024];
1023 CONF_SECTION *cs, *subcs;
1029 token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1030 if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1031 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1032 lexer->filename, lexer->lineno,
1033 lrad_int2str(rlm_policy_tokens, token, "?"));
1038 * See if we're including all of the files in a subdirectory.
1040 strNcpy(buffer, lexer->filename, sizeof(buffer));
1041 p = strrchr(buffer, '/');
1043 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1045 snprintf(buffer, sizeof(buffer), "%s/%s",
1046 radius_dir, filename);
1050 * Include section calling a module.
1052 debug_tokens("including module section from file %s\n", buffer);
1053 cs = conf_read(lexer->filename, lexer->lineno, buffer, NULL);
1055 return 0; /* it prints out error messages */
1059 * The outer section is called "main", and can be ignored.
1060 * It should be a section, so there should be a subsection.
1062 subcs = cf_subsection_find_next(cs, NULL, NULL);
1064 fprintf(stderr, "%s[%d]: Expected section containing modules\n",
1065 lexer->filename, lexer->lineno);
1066 cf_section_free(&cs);
1070 section_name = cf_section_name1(subcs);
1071 rad_assert(section_name != NULL);
1072 component = lrad_str2int(policy_component_names, section_name,
1073 RLM_COMPONENT_COUNT);
1074 if (component == RLM_COMPONENT_COUNT) {
1075 fprintf(stderr, "%s[%d]: Invalid section name \"%s\"\n",
1076 lexer->filename, lexer->lineno, section_name);
1077 cf_section_free(&cs);
1082 * Compile the module entry.
1084 mc = compile_modgroup(component, subcs, buffer);
1086 cf_section_free(&cs);
1087 return 0; /* more often results in calling exit... */
1090 this = rad_malloc(sizeof(*this));
1091 memset(this, 0, sizeof(*this));
1093 this->item.type = POLICY_TYPE_MODULE;
1094 this->item.lineno = lexer->lineno;
1095 this->component = component;
1099 *tail = (policy_item_t *) this;
1106 * Parse one statement. 'foo = bar', or 'if (...) {...}', or '{...}',
1109 static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
1112 policy_reserved_word_t reserved;
1113 policy_lex_t token, assign;
1114 char lhs[256], rhs[256];
1115 policy_assignment_t *this;
1118 * See what kind of token we have.
1120 token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
1122 case POLICY_LEX_LC_BRACKET:
1123 rcode = parse_block(lexer, tail);
1129 case POLICY_LEX_BARE_WORD:
1130 reserved = lrad_str2int(policy_reserved_words,
1132 POLICY_RESERVED_UNKNOWN);
1134 case POLICY_RESERVED_IF:
1135 if (parse_if(lexer, tail)) {
1141 case POLICY_RESERVED_CONTROL:
1142 case POLICY_RESERVED_REQUEST:
1143 case POLICY_RESERVED_REPLY:
1144 case POLICY_RESERVED_PROXY_REQUEST:
1145 case POLICY_RESERVED_PROXY_REPLY:
1146 if (parse_attribute_block(lexer, tail,
1152 case POLICY_RESERVED_PRINT:
1153 if (parse_print(lexer, tail)) {
1159 case POLICY_RESERVED_RETURN:
1160 if (parse_return(lexer, tail)) {
1166 case POLICY_RESERVED_MODULE:
1167 if (parse_module(lexer, tail)) {
1173 case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
1175 * Is a named policy, parse the reference to it.
1177 if (rlm_policy_find(lexer->policies, lhs) != NULL) {
1178 if (!parse_call(lexer, tail, lhs)) {
1185 const DICT_ATTR *dattr;
1188 * Bare words MUST be dictionary attributes
1191 dattr = dict_attrbyname(lhs);
1193 fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
1194 lexer->filename, lexer->lineno, lhs);
1197 debug_tokens("%s[%d]: Got attribute %s\n",
1198 lexer->filename, lexer->lineno,
1204 fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
1205 lexer->filename, lexer->lineno, lhs);
1207 } /* switch over reserved words */
1211 * Return from nested blocks.
1213 case POLICY_LEX_RC_BRACKET:
1214 policy_lex_push_token(lexer, token);
1215 return 2; /* magic */
1217 case POLICY_LEX_EOF: /* nothing more to do */
1221 fprintf(stderr, "%s[%d]: Unexpected %s\n",
1222 lexer->filename, lexer->lineno,
1223 lrad_int2str(policy_explanations,
1229 * Parse a bare statement.
1231 assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1233 case POLICY_LEX_ASSIGN:
1234 case POLICY_LEX_SET_EQUALS:
1235 case POLICY_LEX_AND_EQUALS:
1236 case POLICY_LEX_OR_EQUALS:
1237 case POLICY_LEX_PLUS_EQUALS:
1241 fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
1242 lexer->filename, lexer->lineno,
1243 lrad_int2str(policy_explanations,
1248 this = rad_malloc(sizeof(*this));
1249 memset(this, 0, sizeof(*this));
1251 this->item.type = POLICY_TYPE_ASSIGNMENT;
1252 this->item.lineno = lexer->lineno;
1254 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1255 if ((token != POLICY_LEX_BARE_WORD) &&
1256 (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
1257 fprintf(stderr, "%s[%d]: Unexpected rhs %s\n",
1258 lexer->filename, lexer->lineno,
1259 lrad_int2str(policy_explanations,
1261 rlm_policy_free_item((policy_item_t *) this);
1264 this->rhs_type = token;
1265 this->rhs = strdup(rhs);
1267 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1269 if (token != POLICY_LEX_EOL) {
1270 fprintf(stderr, "%s[%d]: Expected EOL\n",
1271 lexer->filename, lexer->lineno);
1272 rlm_policy_free_item((policy_item_t *) this);
1275 debug_tokens("[ASSIGN %s %s %s]\n",
1276 lhs, lrad_int2str(rlm_policy_tokens, assign, "?"), rhs);
1279 * Fill in the assignment struct
1281 this->lhs = strdup(lhs);
1282 this->assign = assign;
1284 *tail = (policy_item_t *) this;
1291 * Parse block of statements. The block has already been checked
1292 * to begin with a '{'.
1294 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail)
1299 debug_tokens("[BLOCK] ");
1301 token = policy_lex_file(lexer, 0, NULL, 0);
1302 if (token != POLICY_LEX_LC_BRACKET) {
1303 fprintf(stderr, "%s[%d]: Expected '{'\n",
1304 lexer->filename, lexer->lineno);
1309 while ((rcode = parse_statement(lexer, tail)) != 0) {
1311 token = policy_lex_file(lexer, 0, NULL, 0);
1312 if (token != POLICY_LEX_RC_BRACKET) {
1313 fprintf(stderr, "%s[%d]: Expected '}'\n",
1314 lexer->filename, lexer->lineno);
1319 rad_assert(*tail != NULL);
1320 /* parse_statement must fill this in */
1321 while (*tail) tail = &((*tail)->next);
1326 * Parse statement failed.
1333 * Parse debugging statements
1335 static int parse_debug(policy_lex_file_t *lexer)
1341 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1342 if (token != POLICY_LEX_BARE_WORD) {
1343 fprintf(stderr, "%s[%d]: Bad debug command\n",
1344 lexer->filename, lexer->lineno);
1348 if (strcasecmp(buffer, "none") == 0) {
1349 lexer->debug = POLICY_DEBUG_NONE;
1352 } else if (strcasecmp(buffer, "peek") == 0) {
1353 lexer->debug |= POLICY_DEBUG_PEEK;
1356 } else if (strcasecmp(buffer, "print_tokens") == 0) {
1357 lexer->debug |= POLICY_DEBUG_PRINT_TOKENS;
1360 } else if (strcasecmp(buffer, "print_policy") == 0) {
1361 lexer->debug |= POLICY_DEBUG_PRINT_POLICY;
1364 } else if (strcasecmp(buffer, "evaluate") == 0) {
1365 lexer->debug |= POLICY_DEBUG_EVALUATE;
1370 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1372 if (token != POLICY_LEX_EOL) {
1373 fprintf(stderr, "%s[%d]: Expected EOL\n",
1374 lexer->filename, lexer->lineno);
1378 fprintf(stderr, "%s[%d]: Bad debug command \"%s\"\n",
1379 lexer->filename, lexer->lineno, buffer);
1388 * Parse a named policy "policy foo {...}"
1390 static int parse_named_policy(policy_lex_file_t *lexer)
1395 policy_named_t *this;
1398 debug_tokens("[POLICY] ");
1400 this = rad_malloc(sizeof(*this));
1401 memset(this, 0, sizeof(*this));
1403 this->item.type = POLICY_TYPE_NAMED_POLICY;
1404 this->item.lineno = lexer->lineno;
1406 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
1407 if (token != POLICY_LEX_BARE_WORD) {
1408 fprintf(stderr, "%s[%d]: Expected policy name, got \"%s\"\n",
1409 lexer->filename, lexer->lineno,
1410 lrad_int2str(rlm_policy_tokens, token, "?"));
1411 rlm_policy_free_item((policy_item_t *) this);
1415 dattr = dict_attrbyname(mystring);
1417 fprintf(stderr, "%s[%d]: Invalid policy name \"%s\": it is already defined as a dictionary attribute\n",
1418 lexer->filename, lexer->lineno, mystring);
1419 rlm_policy_free_item((policy_item_t *) this);
1423 this->name = strdup(mystring);
1424 rcode = parse_block(lexer, &(this->policy));
1426 rlm_policy_free_item((policy_item_t *) this);
1431 * And insert it into the tree of policies.
1433 * For now, policy names aren't scoped, they're global.
1435 if (!rlm_policy_insert(lexer->policies, this)) {
1436 fprintf(stderr, "Failed to insert policy \"%s\"\n", this->name);
1437 rlm_policy_free_item((policy_item_t *) this);
1442 * Do NOT add it into the list of parsed expressions!
1443 * The above insertion will take care of freeing it if
1444 * anything goes wrong...
1451 * Parse an "include filename" statement
1453 * FIXME: Tie this file into the CONF_SECTION for HUP handling!
1455 static int parse_include(policy_lex_file_t *lexer)
1459 char filename[1024];
1462 token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1463 if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1464 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1465 lexer->filename, lexer->lineno,
1466 lrad_int2str(rlm_policy_tokens, token, "?"));
1471 * See if we're including all of the files in a subdirectory.
1473 strNcpy(buffer, lexer->filename, sizeof(buffer));
1474 p = strrchr(buffer, '/');
1476 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1478 #ifdef HAVE_DIRENT_H
1479 p = strrchr(p + 1, '/');
1486 dir = opendir(buffer);
1488 fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
1489 lexer->filename, lexer->lineno,
1490 buffer, strerror(errno));
1495 * Read the directory, ignoring "." files.
1497 while ((dp = readdir(dir)) != NULL) {
1500 if (dp->d_name[0] == '.') continue;
1501 if (strchr(dp->d_name, '~') != NULL) continue;
1503 strNcpy(p, dp->d_name,
1504 sizeof(buffer) - (p - buffer));
1506 if ((stat(buffer, &buf) != 0) ||
1507 S_ISDIR(buf.st_mode)) continue;
1509 debug_tokens("\nincluding file %s\n", buffer);
1510 if (!rlm_policy_parse(lexer->policies, buffer)) {
1517 } /* else it must have been a normalx file */
1520 snprintf(buffer, sizeof(buffer), "%s/%s",
1521 radius_dir, filename);
1525 * Handle one include file.
1527 debug_tokens("\nincluding file %s\n", buffer);
1528 if (!rlm_policy_parse(lexer->policies, buffer)) {
1537 * Parse data from a file into a policy language.
1539 int rlm_policy_parse(rbtree_t *policies, const char *filename)
1543 policy_lex_file_t mylexer, *lexer = NULL;
1546 fp = fopen(filename, "r");
1548 fprintf(stderr, "Failed to open %s: %s\n",
1549 filename, strerror(errno));
1554 memset(lexer, 0, sizeof(*lexer));
1555 lexer->filename = filename;
1557 lexer->token = POLICY_LEX_BAD;
1558 lexer->parse = NULL; /* initial input */
1559 lexer->policies = policies;
1564 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1566 case POLICY_LEX_BARE_WORD:
1567 reserved = lrad_str2int(policy_reserved_words,
1569 POLICY_RESERVED_UNKNOWN);
1571 case POLICY_RESERVED_POLICY:
1572 if (!parse_named_policy(lexer)) {
1577 case POLICY_RESERVED_INCLUDE:
1578 if (!parse_include(lexer)) {
1583 case POLICY_RESERVED_DEBUG:
1584 if (!parse_debug(lexer)) {
1590 fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
1591 lexer->filename, lexer->lineno,
1595 } /* switch over reserved words */
1597 case POLICY_LEX_EOF:
1601 fprintf(stderr, "%s[%d]: Illegal input\n",
1602 lexer->filename, lexer->lineno);
1607 } while (token != POLICY_LEX_EOF);
1610 debug_tokens("--------------------------------------------------\n");