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);
92 if (c->next_op == COND_NONE) {
93 rad_assert(c->next == NULL);
98 if (c->next_op == COND_AND) {
99 strlcpy(p, " && ", end - p);
102 } else if (c->next_op == COND_OR) {
103 strlcpy(p, " || ", end - p);
116 * Cast a literal vpt to a value_pair_data
118 static int cast_vpt(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
121 VALUE_PAIR_DATA *data;
123 rad_assert(vpt->type == VPT_TYPE_LITERAL);
125 vp = pairalloc(vpt, da);
126 if (!vp) return false;
128 if (!pairparsevalue(vp, vpt->name)) {
133 vpt->length = vp->length;
134 vpt->vpd = data = talloc(vpt, VALUE_PAIR_DATA);
135 if (!vpt->vpd) return false;
137 vpt->type = VPT_TYPE_DATA;
140 memcpy(data, &vp->data, sizeof(*data));
146 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
147 FR_TOKEN *op, char const **error)
149 const char *p = start;
157 *op = T_DOUBLE_QUOTED_STRING;
161 *op = T_SINGLE_QUOTED_STRING;
165 *op = T_BACK_QUOTED_STRING;
169 *op = T_OP_REG_EQ; /* a bit of a hack. */
174 *out = talloc_array(ctx, char, strlen(start) - 1); /* + 2 - 1 */
175 if (!*out) return -1;
189 *error = "End of string after escape";
214 *error = "Unterminated string";
218 static ssize_t condition_tokenize_word(TALLOC_CTX *ctx, char const *start, char **out,
219 FR_TOKEN *op, char const **error)
222 char const *p = start;
224 if ((*p == '"') || (*p == '\'') || (*p == '`') || (*p == '/')) {
225 return condition_tokenize_string(ctx, start, out, op, error);
229 if (*p == '&') p++; /* special-case &User-Name */
233 * The LHS should really be limited to only a few
234 * things. For now, we allow pretty much anything.
237 *error = "Unexpected escape";
249 * Spaces or special characters delineate the word
251 if (isspace((int) *p) || (*p == '&') || (*p == '|') ||
252 (*p == '!') || (*p == '=') || (*p == '<') || (*p == '>')) {
256 if ((*p == '"') || (*p == '\'') || (*p == '`')) {
257 *error = "Unexpected start of string";
266 *error = "Empty string is invalid";
270 *out = talloc_array(ctx, char, len + 1);
271 memcpy(*out, start, len);
277 static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda, char const **error)
279 char const *p = start;
283 while (isspace((int) *p)) p++; /* skip spaces before condition */
285 if (*p != '<') return 0;
289 while (*q && *q != '>') q++;
291 cast = fr_substr2int(dict_attr_types, p, PW_TYPE_INVALID, q - p);
292 if (cast == PW_TYPE_INVALID) {
293 *error = "Invalid data type in cast";
297 *pda = dict_attrbyvalue(PW_CAST_BASE + cast, 0);
299 *error = "Cannot cast to this data type";
305 while (isspace((int) *q)) q++; /* skip spaces after cast */
311 * Less code means less bugs
313 #define return_P(_x) *error = _x;goto return_p
314 #define return_0(_x) *error = _x;goto return_0
315 #define return_lhs(_x) *error = _x;goto return_lhs
316 #define return_rhs(_x) *error = _x;goto return_rhs
317 #define return_SLEN goto return_slen
320 /** Tokenize a conditional check
322 * @param[in] ctx for talloc
323 * @param[in] start the start of the string to process. Should be "(..."
324 * @param[in] brace look for a closing brace
325 * @param[out] pcond pointer to the returned condition structure
326 * @param[out] error the parse error (if any)
327 * @return length of the string skipped, or when negative, the offset to the offending error
329 static ssize_t condition_tokenize(TALLOC_CTX *ctx, char const *start, int brace, fr_cond_t **pcond, char const **error)
332 const char *p = start;
333 const char *lhs_p, *rhs_p;
336 FR_TOKEN op, lhs_type, rhs_type;
338 c = talloc_zero(ctx, fr_cond_t);
340 rad_assert(c != NULL);
343 while (isspace((int) *p)) p++; /* skip spaces before condition */
346 return_P("Empty condition is invalid");
355 while (isspace((int) *p)) p++; /* skip spaces after negation */
361 return_P("Double negation is invalid");
372 * We've already eaten one layer of
373 * brackets. Go recurse to get more.
375 c->type = COND_TYPE_CHILD;
376 slen = condition_tokenize(c, p, true, &c->data.child, error);
381 if (!c->data.child) {
382 return_P("Empty condition is invalid");
386 while (isspace((int) *p)) p++; /* skip spaces after (COND)*/
388 } else { /* it's a bare FOO==BAR */
390 * We didn't see anything special. The condition must be one of
400 return_P("Conditional check cannot begin with a regular expression");
403 slen = condition_tokenize_cast(p, &c->cast, error);
410 slen = condition_tokenize_word(c, p, &lhs, &lhs_type, error);
416 while (isspace((int)*p)) p++; /* skip spaces after LHS */
419 * We may (or not) have an operator
428 * don't skip the brace. We'll look for it later.
437 return_P("No closing brace at end of string");
445 } else if (((p[0] == '&') && (p[1] == '&')) ||
446 ((p[0] == '|') && (p[1] == '|'))) {
450 return_0("Cannot do cast for existence check");
453 c->type = COND_TYPE_EXISTS;
454 c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
456 return_P("Failed creating exists");
459 } else { /* it's an operator */
463 * The next thing should now be a comparison operator.
466 c->type = COND_TYPE_MAP;
469 return_P("Invalid text. Expected comparison operator");
476 } else if (p[1] == '~') {
482 } else if (p[1] == '*') {
483 if (lhs_type != T_BARE_WORD) {
484 return_P("Cannot use !* on a string");
491 goto invalid_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");
516 return_P("Invalid operator");
544 while (isspace((int) *p)) p++; /* skip spaces after operator */
547 return_P("Expected text after operator");
551 * Cannot have a cast on the RHS
554 return_P("Unexpected cast");
561 slen = condition_tokenize_word(c, p, &rhs, &rhs_type, error);
567 * Sanity checks for regexes.
571 return_P("Expected regular expression");
577 if (p[slen] == 'i') {
582 } else if (!regex && (*p == '/')) {
583 return_P("Unexpected regular expression");
586 c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
587 REQUEST_CURRENT, PAIR_LIST_REQUEST,
588 REQUEST_CURRENT, PAIR_LIST_REQUEST);
590 return_P("Failed creating check");
594 * foo =* bar is just (foo)
595 * foo !* bar is just (!foo)
597 if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
598 value_pair_tmpl_t *vpt;
600 vpt = talloc_steal(c, c->data.map->dst);
601 c->data.map->dst = NULL;
603 talloc_free(c->data.map);
604 c->type = COND_TYPE_EXISTS;
608 * Invert the negation bit.
610 if (op == T_OP_CMP_FALSE) {
611 c->negate = !c->negate;
618 * @todo: check LHS and RHS separately, to
621 if ((c->data.map->src->type == VPT_TYPE_LIST) ||
622 (c->data.map->dst->type == VPT_TYPE_LIST)) {
623 return_0("Cannot use list references in condition");
627 * Check cast type. We can have the RHS
628 * a string if the LHS has a cast. But
629 * if the RHS is an attr, it MUST be the
630 * same type as the LHS.
633 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
634 (c->cast->type != c->data.map->src->da->type)) {
638 if (c->data.map->src->type == VPT_TYPE_REGEX) {
639 return_0("Cannot use cast with regex comparison");
643 * The LHS is a literal which has been cast to a data type.
644 * Cast it to the appropriate data type.
646 if ((c->data.map->dst->type == VPT_TYPE_LITERAL) &&
647 !cast_vpt(c->data.map->dst, c->cast)) {
648 *error = "Failed to parse data";
649 if (lhs) talloc_free(lhs);
650 if (rhs) talloc_free(rhs);
652 return -(lhs_p - start);
656 * Casting to a redundant type means we don't need the cast.
658 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
659 (c->cast->type == c->data.map->dst->da->type)) {
665 * Two attributes? They must be of the same type
667 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
668 (c->data.map->dst->type == VPT_TYPE_ATTR) &&
669 (c->data.map->dst->da->type != c->data.map->src->da->type)) {
671 return_0("Attribute comparisons must be of the same attribute type");
675 * Without a cast, we can't compare "foo" to User-Name,
676 * it has to be done the other way around.
678 if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
679 (c->data.map->dst->type != VPT_TYPE_ATTR)) {
680 *error = "Cannot use attribute reference on right side of condition";
682 if (lhs) talloc_free(lhs);
683 if (rhs) talloc_free(rhs);
689 * Invalid: User-Name == bob
690 * Valid: User-Name == "bob"
692 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
693 (c->data.map->src->type != VPT_TYPE_ATTR) &&
694 (c->data.map->dst->da->type == PW_TYPE_STRING) &&
695 (rhs_type == T_BARE_WORD)) {
696 return_rhs("Must have string as value for attribute");
700 * Quotes around non-string
701 * attributes mean that it's
702 * either xlat, or an exec.
704 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
705 (c->data.map->src->type != VPT_TYPE_ATTR) &&
706 (c->data.map->dst->da->type != PW_TYPE_STRING) &&
707 (c->data.map->dst->da->type != PW_TYPE_OCTETS) &&
708 (c->data.map->dst->da->type != PW_TYPE_DATE) &&
709 (rhs_type == T_SINGLE_QUOTED_STRING)) {
710 *error = "Value must be an unquoted string";
712 if (lhs) talloc_free(lhs);
713 if (rhs) talloc_free(rhs);
715 return -(rhs_p - start);
719 * The LHS has been cast to a data type, and the RHS is a
720 * literal. Cast the RHS to the type of the cast.
722 if (c->cast && (c->data.map->src->type == VPT_TYPE_LITERAL) &&
723 !cast_vpt(c->data.map->src, c->cast)) {
724 return_rhs("Failed to parse data");
728 * The LHS is an attribute, and the RHS is a literal. Cast the
729 * RHS to the data type of the LHS.
731 if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
732 (c->data.map->src->type == VPT_TYPE_LITERAL) &&
733 !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
734 return_rhs("Failed to parse data");
741 while (isspace((int) *p)) p++; /* skip spaces after RHS */
743 } /* parse a condition (COND) or FOO OP BAR*/
750 return_P("Unexpected closing brace");
754 while (isspace((int) *p)) p++; /* skip spaces after closing brace */
760 * End of string is now allowed.
764 return_P("No closing brace at end of string");
770 if (!(((p[0] == '&') && (p[1] == '&')) ||
771 ((p[0] == '|') && (p[1] == '|')))) {
772 *error = "Unexpected text after condition";
774 if (lhs) talloc_free(lhs);
775 if (rhs) talloc_free(rhs);
781 * Recurse to parse the next condition.
787 * May still be looking for a closing brace.
789 slen = condition_tokenize(c, p, brace, &c->next, error);
792 if (lhs) talloc_free(lhs);
793 if (rhs) talloc_free(rhs);
795 return slen - (p - start);
801 * Normalize it before returning it.
806 * (FOO) ... --> FOO ...
808 if ((c->type == COND_TYPE_CHILD) && !c->data.child->next) {
811 child = talloc_steal(ctx, c->data.child);
812 c->data.child = NULL;
814 child->next = talloc_steal(child, c->next);
817 child->next_op = c->next_op;
820 * Set the negation properly
822 if ((c->negate && !child->negate) ||
823 (!c->negate && child->negate)) {
824 child->negate = true;
826 child->negate = false;
834 * (FOO ...) --> FOO ...
836 * But don't do !(FOO || BAR) --> !FOO || BAR
837 * Because that's different.
839 if ((c->type == COND_TYPE_CHILD) &&
840 !c->next && !c->negate) {
843 child = talloc_steal(ctx, c->data.child);
844 c->data.child = NULL;
851 * Normalize negation. This doesn't really make any
852 * difference, but it simplifies the run-time code in
855 if (c->type == COND_TYPE_MAP) {
857 * !FOO !~ BAR --> FOO =~ BAR
859 if (c->negate && (c->data.map->op == T_OP_REG_NE)) {
861 c->data.map->op = T_OP_REG_EQ;
865 * FOO !~ BAR --> !FOO =~ BAR
867 if (!c->negate && (c->data.map->op == T_OP_REG_NE)) {
869 c->data.map->op = T_OP_REG_EQ;
873 * !FOO != BAR --> FOO == BAR
875 if (c->negate && (c->data.map->op == T_OP_NE)) {
877 c->data.map->op = T_OP_CMP_EQ;
881 * This next one catches "LDAP-Group != foo",
882 * which doesn't really work, but this hack fixes it.
884 * FOO != BAR --> !FOO == BAR
886 if (!c->negate && (c->data.map->op == T_OP_NE)) {
888 c->data.map->op = T_OP_CMP_EQ;
892 if (lhs) talloc_free(lhs);
893 if (rhs) talloc_free(rhs);
899 /** Tokenize a conditional check
901 * @param[in] ctx for talloc
902 * @param[in] start the start of the string to process. Should be "(..."
903 * @param[out] head the parsed condition structure
904 * @param[out] error the parse error (if any)
905 * @return length of the string skipped, or when negative, the offset to the offending error
907 ssize_t fr_condition_tokenize(TALLOC_CTX *ctx, char const *start, fr_cond_t **head, char const **error)
909 return condition_tokenize(ctx, start, false, head, error);