2 * parser.c Parse various things
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 2013 Alan DeKok <aland@freeradius.org>
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/parser.h>
27 #include <freeradius-devel/rad_assert.h>
31 #define PW_CAST_BASE (1850)
34 * This file shouldn't use any functions from the server core.
37 size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *c)
41 char *end = buffer + bufsize - 1;
45 *(p++) = '!'; /* FIXME: only allow for child? */
49 case COND_TYPE_EXISTS:
50 rad_assert(c->data.vpt != NULL);
52 len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
53 c->cast->type, "??"));
57 len = radius_tmpl2str(p, end - p, c->data.vpt);
62 rad_assert(c->data.map != NULL);
64 *(p++) = '['; /* for extra-clear debugging */
67 len = snprintf(p, end - p, "<%s>", fr_int2str(dict_attr_types,
68 c->cast->type, "??"));
72 len = radius_map2str(p, end - p, c->data.map);
80 rad_assert(c->data.child != NULL);
82 len = fr_cond_sprint(p, end - p, c->data.child);
88 strlcpy(buffer, "true", bufsize);
89 return strlen(buffer);
92 strlcpy(buffer, "false", bufsize);
93 return strlen(buffer);
100 if (c->next_op == COND_NONE) {
101 rad_assert(c->next == NULL);
106 if (c->next_op == COND_AND) {
107 strlcpy(p, " && ", end - p);
110 } else if (c->next_op == COND_OR) {
111 strlcpy(p, " || ", end - p);
124 * Cast a literal vpt to a value_data_t
126 static int cast_vpt(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
131 rad_assert(vpt->type == VPT_TYPE_LITERAL);
133 vp = pairalloc(vpt, da);
134 if (!vp) return false;
136 if (!pairparsevalue(vp, vpt->name)) {
141 vpt->length = vp->length;
142 vpt->vpd = data = talloc(vpt, value_data_t);
143 if (!vpt->vpd) return false;
145 vpt->type = VPT_TYPE_DATA;
148 if (vp->da->flags.is_pointer) {
149 data->ptr = talloc_steal(vpt, vp->data.ptr);
152 memcpy(data, &vp->data, sizeof(*data));
160 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
161 FR_TOKEN *op, char const **error)
163 char const *p = start;
171 *op = T_DOUBLE_QUOTED_STRING;
175 *op = T_SINGLE_QUOTED_STRING;
179 *op = T_BACK_QUOTED_STRING;
183 *op = T_OP_REG_EQ; /* a bit of a hack. */
188 *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
189 if (!*out) return -1;
203 *error = "End of string after escape";
228 *error = "Unterminated string";
232 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
233 FR_TOKEN *op, char const **error)
236 char const *p = start;
238 if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
239 return condition_tokenize_string(ctx, start, out, op, error);
243 if (*p == '&') p++; /* special-case &User-Name */
247 * The LHS should really be limited to only a few
248 * things. For now, we allow pretty much anything.
251 *error = "Unexpected escape";
263 * Spaces or special characters delineate the word
265 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
266 (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
270 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
271 *error = "Unexpected start of string";
280 *error = "Empty string is invalid";
284 *out = talloc_array(ctx, char, len + 1);
285 memcpy(*out, start, len);
291 static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda, char const **error)
293 char const *p = start;
297 while (isspace((int) *p)) p++; /* skip spaces before condition */
299 if (*p != '<') return 0;
303 while (*q && *q != '>') q++;
305 cast = fr_substr2int(dict_attr_types, p, PW_TYPE_INVALID, q - p);
306 if (cast == PW_TYPE_INVALID) {
307 *error = "Invalid data type in cast";
311 *pda = dict_attrbyvalue(PW_CAST_BASE + cast, 0);
313 *error = "Cannot cast to this data type";
319 while (isspace((int) *q)) q++; /* skip spaces after cast */
325 * Less code means less bugs
327 #define return_P(_x) *error = _x;goto return_p
328 #define return_0(_x) *error = _x;goto return_0
329 #define return_lhs(_x) *error = _x;goto return_lhs
330 #define return_rhs(_x) *error = _x;goto return_rhs
331 #define return_SLEN goto return_slen
334 /** Tokenize a conditional check
336 * @param[in] ctx for talloc
337 * @param[in] start the start of the string to process. Should be "(..."
338 * @param[in] brace look for a closing brace
339 * @param[out] pcond pointer to the returned condition structure
340 * @param[out] error the parse error (if any)
341 * @return length of the string skipped, or when negative, the offset to the offending error
343 static ssize_t condition_tokenize(TALLOC_CTX *ctx, char const *start, int brace, fr_cond_t **pcond, char const **error)
346 char const *p = start;
347 char const *lhs_p, *rhs_p;
350 FR_TOKEN op, lhs_type, rhs_type;
352 c = talloc_zero(ctx, fr_cond_t);
354 rad_assert(c != NULL);
357 while (isspace((int) *p)) p++; /* skip spaces before condition */
360 return_P("Empty condition is invalid");
369 while (isspace((int) *p)) p++; /* skip spaces after negation */
375 return_P("Double negation is invalid");
386 * We've already eaten one layer of
387 * brackets. Go recurse to get more.
389 c->type = COND_TYPE_CHILD;
390 slen = condition_tokenize(c, p, true, &c->data.child, error);
395 if (!c->data.child) {
396 return_P("Empty condition is invalid");
400 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
402 } else { /* it's a bare FOO==BAR */
404 * We didn't see anything special. The condition must be one of
414 return_P("Conditional check cannot begin with a regular expression");
417 slen = condition_tokenize_cast(p, &c->cast, error);
424 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
430 while (isspace((int)*p)) p++; /* skip spaces after LHS */
433 * We may (or not) have an operator
442 * don't skip the brace. We'll look for it later.
451 return_P("No closing brace at end of string");
459 } else if (((p[0] == '&') && (p[1] == '&')) ||
460 ((p[0] == '|') && (p[1] == '|'))) {
464 return_0("Cannot do cast for existence check");
467 if (lhs_type == T_BARE_WORD) {
468 if ((strcmp(lhs, "true") == 0) ||
469 ((lhs[0] == '1') && !lhs[1])) {
470 c->type = COND_TYPE_TRUE;
472 } else if ((strcmp(lhs, "false") == 0) ||
473 ((lhs[0] == '0') && !lhs[1])) {
474 c->type = COND_TYPE_FALSE;
482 c->type = COND_TYPE_EXISTS;
483 c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
485 return_P("Failed creating exists");
489 } else { /* it's an operator */
493 * The next thing should now be a comparison operator.
496 c->type = COND_TYPE_MAP;
499 return_P("Invalid text. Expected comparison operator");
506 } else if (p[1] == '~') {
512 } else if (p[1] == '*') {
513 if (lhs_type != T_BARE_WORD) {
514 return_P("Cannot use !* on a string");
521 goto invalid_operator;
530 } else if (p[1] == '~') {
536 } else if (p[1] == '*') {
537 if (lhs_type != T_BARE_WORD) {
538 return_P("Cannot use =* on a string");
546 return_P("Invalid operator");
574 while (isspace((int) *p)) p++; /* skip spaces after operator */
577 return_P("Expected text after operator");
581 * Cannot have a cast on the RHS
584 return_P("Unexpected cast");
591 slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
597 * Sanity checks for regexes.
601 return_P("Expected regular expression");
607 if (p[slen] == 'i') {
612 } else if (!regex && (*p == '/')) {
613 return_P("Unexpected regular expression");
616 c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
617 REQUEST_CURRENT, PAIR_LIST_REQUEST,
618 REQUEST_CURRENT, PAIR_LIST_REQUEST);
620 return_P("Failed creating check");
624 * foo =* bar is just (foo)
625 * foo !* bar is just (!foo)
627 if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
628 value_pair_tmpl_t *vpt;
630 vpt = talloc_steal(c, c->data.map->dst);
631 c->data.map->dst = NULL;
633 talloc_free(c->data.map);
634 c->type = COND_TYPE_EXISTS;
638 * Invert the negation bit.
640 if (op == T_OP_CMP_FALSE) {
641 c->negate = !c->negate;
648 * @todo: check LHS and RHS separately, to
651 if ((c->data.map->src->type == VPT_TYPE_LIST) ||
652 (c->data.map->dst->type == VPT_TYPE_LIST)) {
653 return_0("Cannot use list references in condition");
657 * Check cast type. We can have the RHS
658 * a string if the LHS has a cast. But
659 * if the RHS is an attr, it MUST be the
660 * same type as the LHS.
663 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
664 (c->cast->type != c->data.map->src->da->type)) {
668 if (c->data.map->src->type == VPT_TYPE_REGEX) {
669 return_0("Cannot use cast with regex comparison");
673 * The LHS is a literal which has been cast to a data type.
674 * Cast it to the appropriate data type.
676 if ((c->data.map->dst->type == VPT_TYPE_LITERAL) &&
677 !cast_vpt(c->data.map->dst, c->cast)) {
678 *error = "Failed to parse data";
679 if (lhs) talloc_free(lhs);
680 if (rhs) talloc_free(rhs);
682 return -(lhs_p - start);
686 * The RHS is a literal, and the LHS has been cast to a data
689 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
690 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
691 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
692 return_rhs("Failed to parse data");
696 * Casting to a redundant type means we don't need the cast.
698 * Do this LAST, as the rest of the code above assumes c->cast
701 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
702 (c->cast->type == c->data.map->dst->da->type)) {
708 * Two attributes? They must be of the same type
710 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
711 (c->data.map->dst->type == VPT_TYPE_ATTR) &&
712 (c->data.map->dst->da->type != c->data.map->src->da->type)) {
714 return_0("Attribute comparisons must be of the same attribute type");
718 * Without a cast, we can't compare "foo" to User-Name,
719 * it has to be done the other way around.
721 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
722 (c->data.map->dst->type != VPT_TYPE_ATTR)) {
723 *error = "Cannot use attribute reference on right side of condition";
725 if (lhs) talloc_free(lhs);
726 if (rhs) talloc_free(rhs);
732 * Invalid: User-Name == bob
733 * Valid: User-Name == "bob"
735 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
736 (c->data.map->src->type != VPT_TYPE_ATTR) &&
737 (c->data.map->dst->da->type == PW_TYPE_STRING) &&
738 (rhs_type == T_BARE_WORD)) {
739 return_rhs("Must have string as value for attribute");
743 * Quotes around non-string
744 * attributes mean that it's
745 * either xlat, or an exec.
747 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
748 (c->data.map->src->type != VPT_TYPE_ATTR) &&
749 (c->data.map->dst->da->type != PW_TYPE_STRING) &&
750 (c->data.map->dst->da->type != PW_TYPE_OCTETS) &&
751 (c->data.map->dst->da->type != PW_TYPE_DATE) &&
752 (rhs_type == T_SINGLE_QUOTED_STRING)) {
753 *error = "Value must be an unquoted string";
755 if (lhs) talloc_free(lhs);
756 if (rhs) talloc_free(rhs);
758 return -(rhs_p - start);
762 * The LHS has been cast to a data type, and the RHS is a
763 * literal. Cast the RHS to the type of the cast.
765 if (c->cast && (c->data.map->src->type == VPT_TYPE_LITERAL) &&
766 !cast_vpt(c->data.map->src, c->cast)) {
767 return_rhs("Failed to parse data");
771 * The LHS is an attribute, and the RHS is a literal. Cast the
772 * RHS to the data type of the LHS.
774 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
775 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
776 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
777 return_rhs("Failed to parse data");
784 while (isspace((int) *p)) p++; /* skip spaces after RHS */
786 } /* parse a condition (COND) or FOO OP BAR*/
793 return_P("Unexpected closing brace");
797 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
803 * End of string is now allowed.
807 return_P("No closing brace at end of string");
813 if (!(((p[0] == '&') && (p[1] == '&')) ||
814 ((p[0] == '|') && (p[1] == '|')))) {
815 *error = "Unexpected text after condition";
817 if (lhs) talloc_free(lhs);
818 if (rhs) talloc_free(rhs);
824 * Recurse to parse the next condition.
830 * May still be looking for a closing brace.
832 slen = condition_tokenize(c, p, brace, &c->next, error);
835 if (lhs) talloc_free(lhs);
836 if (rhs) talloc_free(rhs);
838 return slen - (p - start);
844 * Normalize it before returning it.
849 * (FOO) ... --> FOO ...
851 if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
854 child = talloc_steal(ctx, c->data.child);
855 c->data.child = NULL;
857 child->next = talloc_steal(child, c->next);
860 child->next_op = c->next_op;
863 * Set the negation properly
865 if ((c->negate && !child->negate) ||
866 (!c->negate && child->negate)) {
867 child->negate = true;
869 child->negate = false;
878 * (FOO ...) --> FOO ...
880 * But don't do !(FOO || BAR) --> !FOO || BAR
881 * Because that's different.
883 if ((c->type == COND_TYPE_CHILD) &&
884 !c->next && !c->negate) {
887 child = talloc_steal(ctx, c->data.child);
888 c->data.child = NULL;
896 * Normalize negation. This doesn't really make any
897 * difference, but it simplifies the run-time code in
900 if (c->type == COND_TYPE_MAP) {
902 * !FOO !~ BAR --> FOO =~ BAR
904 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
906 c->data.map->op = T_OP_REG_EQ;
910 * FOO !~ BAR --> !FOO =~ BAR
912 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
914 c->data.map->op = T_OP_REG_EQ;
918 * !FOO != BAR --> FOO == BAR
920 if (c->negate && (c->data.map->op == T_OP_NE)) {
922 c->data.map->op = T_OP_CMP_EQ;
926 * This next one catches "LDAP-Group != foo",
927 * which doesn't really work, but this hack fixes it.
929 * FOO != BAR --> !FOO == BAR
931 if (!c->negate && (c->data.map->op == T_OP_NE)) {
933 c->data.map->op = T_OP_CMP_EQ;
936 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
937 (c->data.map->src->type == VPT_TYPE_DATA)) {
940 rad_assert(c->cast != NULL);
942 rcode = radius_evaluate_map(NULL, 0, 0, c->data.map,
945 talloc_free(c->data.map);
950 c->type = COND_TYPE_TRUE;
952 c->type = COND_TYPE_FALSE;
957 if (c->type == COND_TYPE_TRUE) {
960 c->type = COND_TYPE_FALSE;
964 if (c->type == COND_TYPE_FALSE) {
967 c->type = COND_TYPE_TRUE;
972 * true && FOO --> FOO
974 if ((c->type == COND_TYPE_TRUE) &&
975 (c->next_op == COND_AND)) {
978 next = talloc_steal(ctx, c->next);
987 * false && FOO --> false
989 if ((c->type == COND_TYPE_FALSE) &&
990 (c->next_op == COND_AND)) {
991 talloc_free(c->next);
993 c->next_op = COND_NONE;
997 * false || FOO --> FOO
999 if ((c->type == COND_TYPE_FALSE) &&
1000 (c->next_op == COND_OR)) {
1003 next = talloc_steal(ctx, c->next);
1012 * true || FOO --> true
1014 if ((c->type == COND_TYPE_TRUE) &&
1015 (c->next_op == COND_OR)) {
1016 talloc_free(c->next);
1018 c->next_op = COND_NONE;
1021 if (lhs) talloc_free(lhs);
1022 if (rhs) talloc_free(rhs);
1028 /** Tokenize a conditional check
1030 * @param[in] ctx for talloc
1031 * @param[in] start the start of the string to process. Should be "(..."
1032 * @param[out] head the parsed condition structure
1033 * @param[out] error the parse error (if any)
1034 * @return length of the string skipped, or when negative, the offset to the offending error
1036 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
1038 return condition_tokenize(ctx, start, false, head, error);