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_pair_data
126 static int cast_vpt(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
129 VALUE_PAIR_DATA *data;
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_PAIR_DATA);
143 if (!vpt->vpd) return false;
145 vpt->type = VPT_TYPE_DATA;
148 memcpy(data, &vp->data, sizeof(*data));
154 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
155 FR_TOKEN *op, char const **error)
157 const char *p = start;
165 *op = T_DOUBLE_QUOTED_STRING;
169 *op = T_SINGLE_QUOTED_STRING;
173 *op = T_BACK_QUOTED_STRING;
177 *op = T_OP_REG_EQ; /* a bit of a hack. */
182 *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
183 if (!*out) return -1;
197 *error = "End of string after escape";
222 *error = "Unterminated string";
226 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
227 FR_TOKEN *op, char const **error)
230 char const *p = start;
232 if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
233 return condition_tokenize_string(ctx, start, out, op, error);
237 if (*p == '&') p++; /* special-case &User-Name */
241 * The LHS should really be limited to only a few
242 * things. For now, we allow pretty much anything.
245 *error = "Unexpected escape";
257 * Spaces or special characters delineate the word
259 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
260 (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
264 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
265 *error = "Unexpected start of string";
274 *error = "Empty string is invalid";
278 *out = talloc_array(ctx, char, len + 1);
279 memcpy(*out, start, len);
285 static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda, char const **error)
287 char const *p = start;
291 while (isspace((int) *p)) p++; /* skip spaces before condition */
293 if (*p != '<') return 0;
297 while (*q && *q != '>') q++;
299 cast = fr_substr2int(dict_attr_types, p, PW_TYPE_INVALID, q - p);
300 if (cast == PW_TYPE_INVALID) {
301 *error = "Invalid data type in cast";
305 *pda = dict_attrbyvalue(PW_CAST_BASE + cast, 0);
307 *error = "Cannot cast to this data type";
313 while (isspace((int) *q)) q++; /* skip spaces after cast */
319 * Less code means less bugs
321 #define return_P(_x) *error = _x;goto return_p
322 #define return_0(_x) *error = _x;goto return_0
323 #define return_lhs(_x) *error = _x;goto return_lhs
324 #define return_rhs(_x) *error = _x;goto return_rhs
325 #define return_SLEN goto return_slen
328 /** Tokenize a conditional check
330 * @param[in] ctx for talloc
331 * @param[in] start the start of the string to process. Should be "(..."
332 * @param[in] brace look for a closing brace
333 * @param[out] pcond pointer to the returned condition structure
334 * @param[out] error the parse error (if any)
335 * @return length of the string skipped, or when negative, the offset to the offending error
337 static ssize_t condition_tokenize(TALLOC_CTX *ctx, char const *start, int brace, fr_cond_t **pcond, char const **error)
340 const char *p = start;
341 const char *lhs_p, *rhs_p;
344 FR_TOKEN op, lhs_type, rhs_type;
346 c = talloc_zero(ctx, fr_cond_t);
348 rad_assert(c != NULL);
351 while (isspace((int) *p)) p++; /* skip spaces before condition */
354 return_P("Empty condition is invalid");
363 while (isspace((int) *p)) p++; /* skip spaces after negation */
369 return_P("Double negation is invalid");
380 * We've already eaten one layer of
381 * brackets. Go recurse to get more.
383 c->type = COND_TYPE_CHILD;
384 slen = condition_tokenize(c, p, true, &c->data.child, error);
389 if (!c->data.child) {
390 return_P("Empty condition is invalid");
394 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
396 } else { /* it's a bare FOO==BAR */
398 * We didn't see anything special. The condition must be one of
408 return_P("Conditional check cannot begin with a regular expression");
411 slen = condition_tokenize_cast(p, &c->cast, error);
418 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
424 while (isspace((int)*p)) p++; /* skip spaces after LHS */
427 * We may (or not) have an operator
436 * don't skip the brace. We'll look for it later.
445 return_P("No closing brace at end of string");
453 } else if (((p[0] == '&') && (p[1] == '&')) ||
454 ((p[0] == '|') && (p[1] == '|'))) {
458 return_0("Cannot do cast for existence check");
461 if (lhs_type == T_BARE_WORD) {
462 if ((strcmp(lhs, "true") == 0) ||
463 ((lhs[0] == '1') && !lhs[1])) {
464 c->type = COND_TYPE_TRUE;
466 } else if ((strcmp(lhs, "false") == 0) ||
467 ((lhs[0] == '0') && !lhs[1])) {
468 c->type = COND_TYPE_FALSE;
476 c->type = COND_TYPE_EXISTS;
477 c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
479 return_P("Failed creating exists");
483 } else { /* it's an operator */
487 * The next thing should now be a comparison operator.
490 c->type = COND_TYPE_MAP;
493 return_P("Invalid text. Expected comparison operator");
500 } else if (p[1] == '~') {
506 } else if (p[1] == '*') {
507 if (lhs_type != T_BARE_WORD) {
508 return_P("Cannot use !* on a string");
515 goto invalid_operator;
524 } else if (p[1] == '~') {
530 } else if (p[1] == '*') {
531 if (lhs_type != T_BARE_WORD) {
532 return_P("Cannot use =* on a string");
540 return_P("Invalid operator");
568 while (isspace((int) *p)) p++; /* skip spaces after operator */
571 return_P("Expected text after operator");
575 * Cannot have a cast on the RHS
578 return_P("Unexpected cast");
585 slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
591 * Sanity checks for regexes.
595 return_P("Expected regular expression");
601 if (p[slen] == 'i') {
606 } else if (!regex && (*p == '/')) {
607 return_P("Unexpected regular expression");
610 c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
611 REQUEST_CURRENT, PAIR_LIST_REQUEST,
612 REQUEST_CURRENT, PAIR_LIST_REQUEST);
614 return_P("Failed creating check");
618 * foo =* bar is just (foo)
619 * foo !* bar is just (!foo)
621 if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
622 value_pair_tmpl_t *vpt;
624 vpt = talloc_steal(c, c->data.map->dst);
625 c->data.map->dst = NULL;
627 talloc_free(c->data.map);
628 c->type = COND_TYPE_EXISTS;
632 * Invert the negation bit.
634 if (op == T_OP_CMP_FALSE) {
635 c->negate = !c->negate;
642 * @todo: check LHS and RHS separately, to
645 if ((c->data.map->src->type == VPT_TYPE_LIST) ||
646 (c->data.map->dst->type == VPT_TYPE_LIST)) {
647 return_0("Cannot use list references in condition");
651 * Check cast type. We can have the RHS
652 * a string if the LHS has a cast. But
653 * if the RHS is an attr, it MUST be the
654 * same type as the LHS.
657 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
658 (c->cast->type != c->data.map->src->da->type)) {
662 if (c->data.map->src->type == VPT_TYPE_REGEX) {
663 return_0("Cannot use cast with regex comparison");
667 * The LHS is a literal which has been cast to a data type.
668 * Cast it to the appropriate data type.
670 if ((c->data.map->dst->type == VPT_TYPE_LITERAL) &&
671 !cast_vpt(c->data.map->dst, c->cast)) {
672 *error = "Failed to parse data";
673 if (lhs) talloc_free(lhs);
674 if (rhs) talloc_free(rhs);
676 return -(lhs_p - start);
680 * The RHS is a literal, and the LHS has been cast to a data
683 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
684 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
685 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
686 return_rhs("Failed to parse data");
690 * Casting to a redundant type means we don't need the cast.
692 * Do this LAST, as the rest of the code above assumes c->cast
695 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
696 (c->cast->type == c->data.map->dst->da->type)) {
702 * Two attributes? They must be of the same type
704 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
705 (c->data.map->dst->type == VPT_TYPE_ATTR) &&
706 (c->data.map->dst->da->type != c->data.map->src->da->type)) {
708 return_0("Attribute comparisons must be of the same attribute type");
712 * Without a cast, we can't compare "foo" to User-Name,
713 * it has to be done the other way around.
715 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
716 (c->data.map->dst->type != VPT_TYPE_ATTR)) {
717 *error = "Cannot use attribute reference on right side of condition";
719 if (lhs) talloc_free(lhs);
720 if (rhs) talloc_free(rhs);
726 * Invalid: User-Name == bob
727 * Valid: User-Name == "bob"
729 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
730 (c->data.map->src->type != VPT_TYPE_ATTR) &&
731 (c->data.map->dst->da->type == PW_TYPE_STRING) &&
732 (rhs_type == T_BARE_WORD)) {
733 return_rhs("Must have string as value for attribute");
737 * Quotes around non-string
738 * attributes mean that it's
739 * either xlat, or an exec.
741 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
742 (c->data.map->src->type != VPT_TYPE_ATTR) &&
743 (c->data.map->dst->da->type != PW_TYPE_STRING) &&
744 (c->data.map->dst->da->type != PW_TYPE_OCTETS) &&
745 (c->data.map->dst->da->type != PW_TYPE_DATE) &&
746 (rhs_type == T_SINGLE_QUOTED_STRING)) {
747 *error = "Value must be an unquoted string";
749 if (lhs) talloc_free(lhs);
750 if (rhs) talloc_free(rhs);
752 return -(rhs_p - start);
756 * The LHS has been cast to a data type, and the RHS is a
757 * literal. Cast the RHS to the type of the cast.
759 if (c->cast && (c->data.map->src->type == VPT_TYPE_LITERAL) &&
760 !cast_vpt(c->data.map->src, c->cast)) {
761 return_rhs("Failed to parse data");
765 * The LHS is an attribute, and the RHS is a literal. Cast the
766 * RHS to the data type of the LHS.
768 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
769 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
770 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
771 return_rhs("Failed to parse data");
778 while (isspace((int) *p)) p++; /* skip spaces after RHS */
780 } /* parse a condition (COND) or FOO OP BAR*/
787 return_P("Unexpected closing brace");
791 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
797 * End of string is now allowed.
801 return_P("No closing brace at end of string");
807 if (!(((p[0] == '&') && (p[1] == '&')) ||
808 ((p[0] == '|') && (p[1] == '|')))) {
809 *error = "Unexpected text after condition";
811 if (lhs) talloc_free(lhs);
812 if (rhs) talloc_free(rhs);
818 * Recurse to parse the next condition.
824 * May still be looking for a closing brace.
826 slen = condition_tokenize(c, p, brace, &c->next, error);
829 if (lhs) talloc_free(lhs);
830 if (rhs) talloc_free(rhs);
832 return slen - (p - start);
838 * Normalize it before returning it.
843 * (FOO) ... --> FOO ...
845 if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
848 child = talloc_steal(ctx, c->data.child);
849 c->data.child = NULL;
851 child->next = talloc_steal(child, c->next);
854 child->next_op = c->next_op;
857 * Set the negation properly
859 if ((c->negate && !child->negate) ||
860 (!c->negate && child->negate)) {
861 child->negate = true;
863 child->negate = false;
872 * (FOO ...) --> FOO ...
874 * But don't do !(FOO || BAR) --> !FOO || BAR
875 * Because that's different.
877 if ((c->type == COND_TYPE_CHILD) &&
878 !c->next && !c->negate) {
881 child = talloc_steal(ctx, c->data.child);
882 c->data.child = NULL;
890 * Normalize negation. This doesn't really make any
891 * difference, but it simplifies the run-time code in
894 if (c->type == COND_TYPE_MAP) {
896 * !FOO !~ BAR --> FOO =~ BAR
898 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
900 c->data.map->op = T_OP_REG_EQ;
904 * FOO !~ BAR --> !FOO =~ BAR
906 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
908 c->data.map->op = T_OP_REG_EQ;
912 * !FOO != BAR --> FOO == BAR
914 if (c->negate && (c->data.map->op == T_OP_NE)) {
916 c->data.map->op = T_OP_CMP_EQ;
920 * This next one catches "LDAP-Group != foo",
921 * which doesn't really work, but this hack fixes it.
923 * FOO != BAR --> !FOO == BAR
925 if (!c->negate && (c->data.map->op == T_OP_NE)) {
927 c->data.map->op = T_OP_CMP_EQ;
930 if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
931 (c->data.map->src->type == VPT_TYPE_DATA)) {
934 rad_assert(c->cast != NULL);
936 rcode = radius_evaluate_map(NULL, 0, 0, c->data.map,
939 talloc_free(c->data.map);
944 c->type = COND_TYPE_TRUE;
946 c->type = COND_TYPE_FALSE;
951 if (c->type == COND_TYPE_TRUE) {
954 c->type = COND_TYPE_FALSE;
958 if (c->type == COND_TYPE_FALSE) {
961 c->type = COND_TYPE_TRUE;
966 * true && FOO --> FOO
968 if ((c->type == COND_TYPE_TRUE) &&
969 (c->next_op == COND_AND)) {
972 next = talloc_steal(ctx, c->next);
981 * false && FOO --> false
983 if ((c->type == COND_TYPE_FALSE) &&
984 (c->next_op == COND_AND)) {
985 talloc_free(c->next);
987 c->next_op = COND_NONE;
991 * false || FOO --> FOO
993 if ((c->type == COND_TYPE_FALSE) &&
994 (c->next_op == COND_OR)) {
997 next = talloc_steal(ctx, c->next);
1006 * true || FOO --> true
1008 if ((c->type == COND_TYPE_TRUE) &&
1009 (c->next_op == COND_OR)) {
1010 talloc_free(c->next);
1012 c->next_op = COND_NONE;
1015 if (lhs) talloc_free(lhs);
1016 if (rhs) talloc_free(rhs);
1022 /** Tokenize a conditional check
1024 * @param[in] ctx for talloc
1025 * @param[in] start the start of the string to process. Should be "(..."
1026 * @param[out] head the parsed condition structure
1027 * @param[out] error the parse error (if any)
1028 * @return length of the string skipped, or when negative, the offset to the offending error
1030 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
1032 return condition_tokenize(ctx, start, false, head, error);