More escaping / unescaping fixes.
authorAlan T. DeKok <aland@freeradius.org>
Sat, 6 Dec 2014 15:17:06 +0000 (10:17 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Sat, 6 Dec 2014 15:18:14 +0000 (10:18 -0500)
The template names are now binary-safe if cf_new_escape

The various parsing functions take a quotation character, so that
they can do the correct de-escaping.

The tests have been audited and updated.

14 files changed:
src/include/libradius.h
src/include/radiusd.h
src/include/tmpl.h
src/lib/pair.c
src/lib/value.c
src/main/evaluate.c
src/main/map.c
src/main/modcall.c
src/main/parser.c
src/main/tmpl.c
src/main/xlat.c
src/tests/keywords/escape
src/tests/keywords/escape-sequences
src/tests/unit/condition.txt

index fe6f3dd..3cc897d 100644 (file)
@@ -657,7 +657,7 @@ int         value_data_cmp_op(FR_TOKEN op,
 
 ssize_t                value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                                    PW_TYPE *src_type, DICT_ATTR const *src_enumv,
-                                   char const *src, ssize_t src_len);
+                                   char const *src, ssize_t src_len, char quote);
 
 ssize_t                value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
                                PW_TYPE dst_type, DICT_ATTR const *dst_enumv,
index 04cd38f..3cdd430 100644 (file)
@@ -748,7 +748,7 @@ void mark_home_server_dead(home_server_t *home, struct timeval *when);
 
 /* evaluate.c */
 typedef struct fr_cond_t fr_cond_t;
-ssize_t radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt);
+ssize_t radius_expand_tmpl(value_data_t *vd, REQUEST *request, value_pair_tmpl_t const *vpt);
 int radius_evaluate_tmpl(REQUEST *request, int modreturn, int depth,
                         value_pair_tmpl_t const *vpt);
 int radius_evaluate_map(REQUEST *request, int modreturn, int depth,
index 82629c8..d7ef7e0 100644 (file)
@@ -226,7 +226,7 @@ ssize_t                     tmpl_afrom_attr_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char con
 /*
  *     Parses any type of string into a template
  */
-ssize_t                        tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name,
+ssize_t                        tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name, size_t inlen,
                                       FR_TOKEN type, request_refs_t request_def, pair_lists_t list_def);
 
 void                   tmpl_free(value_pair_tmpl_t **tmpl);
index ab3dbf0..40ed85e 100644 (file)
@@ -1112,7 +1112,11 @@ int pairparsevalue(VALUE_PAIR *vp, char const *value, size_t inlen)
 
        type = vp->da->type;
 
-       ret = value_data_from_str(vp, &vp->data, &type, vp->da, value, inlen);
+       /*
+        *      We presume that the input data is from a double quoted
+        *      string, and needs escaping
+        */
+       ret = value_data_from_str(vp, &vp->data, &type, vp->da, value, inlen, '"');
        if (ret < 0) return -1;
 
        /*
index ee40aa0..89e766d 100644 (file)
@@ -423,7 +423,7 @@ static char const hextab[] = "0123456789abcdef";
  */
 ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                            PW_TYPE *src_type, DICT_ATTR const *src_enumv,
-                           char const *src, ssize_t src_len)
+                           char const *src, ssize_t src_len, char quote)
 {
        DICT_VALUE      *dval;
        size_t          len;
@@ -446,86 +446,131 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
        switch (*src_type) {
        case PW_TYPE_STRING:
        {
-               size_t          p_len;
-               char const      *cp;
                char            *p;
+               char const      *q;
                int             x;
 
-               /*
-                *      Do escaping here
-                */
                dst->strvalue = p = talloc_memdup(ctx, src, len + 1);
                p[len] = '\0';
                talloc_set_type(p, char);
 
-               cp = src;
-               p_len = 0;
-               while (*cp) {
-                       char c = *cp++;
-
-                       if (c == '\\') switch (*cp) {
-                       case 'r':
-                               c = '\r';
-                               cp++;
-                               break;
-
-                       case 'n':
-                               c = '\n';
-                               cp++;
-                               break;
-
-                       case 't':
-                               c = '\t';
-                               cp++;
-                               break;
-
-                       case '"':
-                               c = '"';
-                               cp++;
-                               break;
-
-                       case '\'':
-                               c = '\'';
-                               cp++;
-                               break;
-
-                       case '\\':
-                               c = '\\';
-                               cp++;
-                               break;
-
-                       case '`':
-                               c = '`';
-                               cp++;
-                               break;
-
-                       case '\0':
-                               c = '\\'; /* no cp++ */
-                               break;
-                       default:
-                               if ((cp[0] >= '0') &&
-                                   (cp[0] <= '9') &&
-                                   (cp[1] >= '0') &&
-                                   (cp[1] <= '9') &&
-                                   (cp[2] >= '0') &&
-                                   (cp[2] <= '9') &&
-                                   (sscanf(cp, "%3o", &x) == 1)) {
-                                       c = x;
-                                       cp += 3;
+               /*
+                *      No de-quoting.  Just copy the string.
+                */
+               if (!quote) {
+                       ret = len;
+                       goto finish;
+               }
+
+               /*
+                *      Do escaping for single quoted strings.  Only
+                *      single quotes get escaped.  Everything else is
+                *      left as-is.
+                */
+               if (quote == '\'') {
+                       q = p;
+
+                       /*
+                        *      Escape ONLY the quotation character.
+                        *      Everything else is left as-is.
+                        */
+                       while (q < (dst->strvalue + len)) {
+                               if ((q[0] == '\\') &&
+                                   (q[1] == '\'')) {
+                                       *(p++) = '\'';
+                                       q += 2;
+                                       continue;
                                }
 
                                /*
-                                *      Else It's not a recognised escape sequence DON'T
-                                *      consume the backslash. This is identical
-                                *      behaviour to bash and most other things that
-                                *      use backslash escaping.
+                                *      Not escaped, just copy it over.
                                 */
+                               *(p++) = *(q++);
+                       }
+
+                       *p = '\0';
+                       ret = p - dst->strvalue;
+                       dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+                       goto finish;
+               }
+
+               /*
+                *      It's "string" or `string`, do all standard
+                *      escaping.
+                */
+               q = p;
+               while (q < (dst->strvalue + len)) {
+                       char c = *q++;
+
+                       if ((c == '\\') && (q >= (dst->strvalue + len))) {
+                               fr_strerror_printf("Invalid escape at end of string");
+                               return -1;
                        }
+
+                       /*
+                        *      Fix up \X -> ... the binary form of it.
+                        */
+                       if (c == '\\') {
+                               switch (*q) {
+                               case 'r':
+                                       c = '\r';
+                                       q++;
+                                       break;
+
+                               case 'n':
+                                       c = '\n';
+                                       q++;
+                                       break;
+
+                               case 't':
+                                       c = '\t';
+                                       q++;
+                                       break;
+
+                               case '\\':
+                                       c = '\\';
+                                       q++;
+                                       break;
+
+                               default:
+                                       /*
+                                        *      \" --> ", but only inside of double quoted strings, etc.
+                                        */
+                                       if (*q == quote) {
+                                               c = quote;
+                                               q++;
+                                               break;
+                                       }
+
+                                       /*
+                                        *      \000 --> binary zero character
+                                        */
+                                       if ((q[0] >= '0') &&
+                                           (q[0] <= '9') &&
+                                           (q[1] >= '0') &&
+                                           (q[1] <= '9') &&
+                                           (q[2] >= '0') &&
+                                           (q[2] <= '9') &&
+                                           (sscanf(q, "%3o", &x) == 1)) {
+                                               c = x;
+                                               q += 3;
+                                       }
+
+                                       /*
+                                        *      Else It's not a recognised escape sequence DON'T
+                                        *      consume the backslash. This is identical
+                                        *      behaviour to bash and most other things that
+                                        *      use backslash escaping.
+                                        */
+                               }
+                       }
+
                        *p++ = c;
-                       p_len++;
                }
+
                *p = '\0';
-               ret = p_len;
+               ret = p - dst->strvalue;
+               dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
        }
                goto finish;
 
@@ -982,7 +1027,7 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
         *      Deserialise a value_data_t
         */
        if (src_type == PW_TYPE_STRING) {
-               return value_data_from_str(ctx, dst, &dst_type, dst_enumv, src->strvalue, src_len);
+               return value_data_from_str(ctx, dst, &dst_type, dst_enumv, src->strvalue, src_len, '\0');
        }
 
        /*
index b4c33c8..1e37bb1 100644 (file)
@@ -70,16 +70,16 @@ static bool all_digits(char const *string)
  *
  * @note Length of expanded string can be found with talloc_array_length(*out) - 1
  *
- * @param out where to write a pointer to the newly allocated buffer.
+ * @param vd the output string
  * @param request Current request.
  * @param vpt to evaluate.
  * @return -1 on error, else 0.
  */
-ssize_t radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
+ssize_t radius_expand_tmpl(value_data_t *vd, REQUEST *request, value_pair_tmpl_t const *vpt)
 {
        VALUE_PAIR *vp;
        ssize_t slen;
-       *out = NULL;
+       char *out = NULL;
 
        rad_assert(vpt->type != TMPL_TYPE_LIST);
 
@@ -88,43 +88,39 @@ ssize_t radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const
        switch (vpt->type) {
        case TMPL_TYPE_LITERAL:
                EVAL_DEBUG("EXPAND TMPL LITERAL");
-               *out = talloc_typed_strdup(request, vpt->name);
-               slen = talloc_array_length(*out) - 1;
-               break;
+               vd->strvalue = talloc_memdup(request, vpt->name, vpt->len);
+               return vpt->len;
 
        case TMPL_TYPE_EXEC:
                EVAL_DEBUG("EXPAND TMPL EXEC");
-               *out = talloc_array(request, char, 1024);
-               if (radius_exec_program(*out, 1024, NULL, request, vpt->name, NULL, true, false, EXEC_TIMEOUT) != 0) {
-                       TALLOC_FREE(*out);
+               out = talloc_array(request, char, 1024);
+               if (radius_exec_program(out, 1024, NULL, request, vpt->name, NULL, true, false, EXEC_TIMEOUT) != 0) {
+                       TALLOC_FREE(out);
                        return -1;
                }
-               slen = strlen(*out);
+               slen = strlen(out);
                break;
 
        case TMPL_TYPE_XLAT:
                EVAL_DEBUG("EXPAND TMPL XLAT");
                /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_axlat(out, request, vpt->name, NULL, NULL);
+               slen = radius_axlat(&out, request, vpt->name, NULL, NULL);
                if (slen < 0) {
-                       rad_assert(!*out);
+                       rad_assert(!out);
                        return slen;
                }
-               slen = strlen(*out);
+               slen = strlen(out);
                break;
 
        case TMPL_TYPE_XLAT_STRUCT:
                EVAL_DEBUG("EXPAND TMPL XLAT STRUCT");
                /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_axlat_struct(out, request, vpt->tmpl_xlat, NULL, NULL);
+               slen = radius_axlat_struct(&out, request, vpt->tmpl_xlat, NULL, NULL);
                if (slen < 0) {
-                       rad_assert(!*out);
+                       rad_assert(!out);
                        return slen;
                }
-               slen = strlen(*out);
-
-               RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
-               RDEBUG2("   --> %s", *out);
+               slen = strlen(out);
                break;
 
        case TMPL_TYPE_ATTR:
@@ -135,28 +131,53 @@ ssize_t radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const
                ret = tmpl_find_vp(&vp, request, vpt);
                if (ret < 0) return -2;
 
-               *out = vp_aprints_value(request, vp, '\0');
-               if (!*out) return -1;
-               slen = talloc_array_length(*out) - 1;
+               out = vp_aprints_value(request, vp, '"');
+               if (!out) return -1;
+               slen = talloc_array_length(out) - 1;
        }
                break;
 
        /*
         *      We should never be expanding these.
         */
+       case TMPL_TYPE_UNKNOWN:
+       case TMPL_TYPE_NULL:
+       case TMPL_TYPE_LIST:
        case TMPL_TYPE_DATA:
        case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_ATTR_UNDEFINED:
        case TMPL_TYPE_REGEX_STRUCT:
                rad_assert(0 == 1);
                slen = -1;
-               /* FALL-THROUGH */
-
-       default:
-               slen = 0;
                break;
        }
 
-       EVAL_DEBUG("   --> %s", *out);
+       if (slen < 0) return slen;
+
+       vd->strvalue = out;
+
+       /*
+        *      If we're doing correct escapes, we may have to re-parse the string.
+        *      If the string is from another expansion, it needs re-parsing.
+        *      Or, if it's from a "string" attribute, it needs re-parsing.
+        *      Integers, IP addresses, etc. don't need re-parsing.
+        */
+       if (cf_new_escape &&
+           ((vpt->type != TMPL_TYPE_ATTR) ||
+            (vpt->tmpl_da->type == PW_TYPE_STRING))) {
+               PW_TYPE type = PW_TYPE_STRING;
+
+               slen = value_data_from_str(request, vd, &type, NULL, out, slen, '"');
+               out = vd->ptr;
+       }
+
+       if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
+               RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
+               RDEBUG2("   --> %s", out);
+       } else {
+               EVAL_DEBUG("   --> %s", out);
+       }
+
        return slen;
 }
 
@@ -174,7 +195,7 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, valu
 {
        int rcode;
        int modcode;
-       char *buffer;
+       value_data_t data;
 
        switch (vpt->type) {
        case TMPL_TYPE_LITERAL:
@@ -209,13 +230,13 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, valu
        case TMPL_TYPE_XLAT:
        case TMPL_TYPE_EXEC:
                if (!*vpt->name) return false;
-               rcode = radius_expand_tmpl(&buffer, request, vpt);
+               rcode = radius_expand_tmpl(&data, request, vpt);
                if (rcode < 0) {
                        EVAL_DEBUG("FAIL %d", __LINE__);
                        return -1;
                }
-               rcode = (buffer && (*buffer != '\0'));
-               talloc_free(buffer);
+               rcode = (data.strvalue && (*data.strvalue != '\0'));
+               talloc_free(data.ptr);
                break;
 
                /*
@@ -564,7 +585,7 @@ do {\
                value_data_t data;
 
                if (map->rhs->type != TMPL_TYPE_LITERAL) {
-                       ret = radius_expand_tmpl((char **)&data.ptr, request, map->rhs);
+                       ret = radius_expand_tmpl(&data, request, map->rhs);
                        if (ret < 0) {
                                EVAL_DEBUG("FAIL [%i]", __LINE__);
                                rcode = -1;
@@ -683,7 +704,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                value_data_t data;
 
                if (map->lhs->type != TMPL_TYPE_LITERAL) {
-                       ret = radius_expand_tmpl((char **)&data.ptr, request, map->lhs);
+                       ret = radius_expand_tmpl(&data, request, map->lhs);
                        if (ret < 0) {
                                EVAL_DEBUG("FAIL [%i]", __LINE__);
                                return ret;
index 5251f63..a92e200 100644 (file)
@@ -216,7 +216,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
                /* do nothing */
 
        } else {
-               slen = tmpl_afrom_str(map, &map->rhs, value, type, src_request_def, src_list_def);
+               slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, src_request_def, src_list_def);
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -385,7 +385,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
 
        map = talloc_zero(ctx, value_pair_map_t);
 
-       slen = tmpl_afrom_str(map, &map->lhs, lhs, lhs_type, dst_request_def, dst_list_def);
+       slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def);
        if (slen < 0) {
        error:
                talloc_free(map);
@@ -400,7 +400,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
                return 0;
        }
 
-       slen = tmpl_afrom_str(map, &map->rhs, rhs, rhs_type, src_request_def, src_list_def);
+       slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def);
        if (slen < 0) goto error;
 
        VERIFY_MAP(map);
index d371d46..713d064 100644 (file)
@@ -794,8 +794,6 @@ redo:
                fr_cond_t cond;
                value_pair_map_t map;
                value_pair_tmpl_t vpt;
-               char *expanded = NULL;
-
 
                MOD_LOG_OPEN_BRACE;
 
@@ -841,8 +839,11 @@ redo:
                if ((g->vpt->type == TMPL_TYPE_XLAT_STRUCT) ||
                    (g->vpt->type == TMPL_TYPE_XLAT) ||
                    (g->vpt->type == TMPL_TYPE_EXEC)) {
-                       if (radius_expand_tmpl(&expanded, request, g->vpt) < 0) goto find_null_case;
-                       tmpl_init(&vpt, TMPL_TYPE_LITERAL, expanded, talloc_array_length(expanded) - 1);
+                       value_data_t data;
+
+                       if (radius_expand_tmpl(&data, request, g->vpt) < 0) goto find_null_case;
+                       tmpl_init(&vpt, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
                }
 
                /*
@@ -907,7 +908,6 @@ redo:
                                break;
                        }
                }
-               talloc_free(expanded);
 
                if (!found) found = null_case;
 
@@ -1894,7 +1894,7 @@ static modcallable *do_compile_modswitch (modcallable *parent, rlm_components_t
         *      will fix it up.
         */
        type = cf_section_name2_type(cs);
-       slen = tmpl_afrom_str(cs, &vpt, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
        if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                char *spaces, *text;
 
@@ -1994,7 +1994,7 @@ static modcallable *do_compile_modcase(modcallable *parent, rlm_components_t com
 
                type = cf_section_name2_type(cs);
 
-               slen = tmpl_afrom_str(cs, &vpt, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+               slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
                if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                        char *spaces, *text;
 
@@ -2079,7 +2079,7 @@ static modcallable *do_compile_modforeach(modcallable *parent,
         *      will fix it up.
         */
        type = cf_section_name2_type(cs);
-       slen = tmpl_afrom_str(cs, &vpt, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
        if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                char *spaces, *text;
 
@@ -3295,7 +3295,8 @@ check_paircmp:
                value_pair_tmpl_t *vpt;
 
                fmt = talloc_asprintf(map->lhs, "%%{%s}", map->lhs->name);
-               slen = tmpl_afrom_str(map, &vpt, fmt, T_DOUBLE_QUOTED_STRING, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+               slen = tmpl_afrom_str(map, &vpt, fmt, talloc_array_length(fmt) - 1,
+                                     T_DOUBLE_QUOTED_STRING, REQUEST_CURRENT, PAIR_LIST_REQUEST);
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -3491,7 +3492,7 @@ bool modcall_pass2(modcallable *mc)
                                rad_assert(c->name[0] == '&');
                                rad_assert(cf_section_name2_type(g->cs) == T_BARE_WORD);
 
-                               slen = tmpl_afrom_str(g->cs, &g->vpt, c->name,
+                               slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name),
                                                      cf_section_name2_type(g->cs),
                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
                                if (slen < 0) {
@@ -3537,7 +3538,7 @@ bool modcall_pass2(modcallable *mc)
                        if (g->vpt->type == TMPL_TYPE_LITERAL) {
                                value_pair_tmpl_t *vpt;
 
-                               slen = tmpl_afrom_str(g->cs, &vpt, c->name, cf_section_name2_type(g->cs),
+                               slen = tmpl_afrom_str(g->cs, &vpt, c->name, strlen(c->name), cf_section_name2_type(g->cs),
                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
                                if (slen < 0) goto parse_error;
                                if (vpt->type == TMPL_TYPE_ATTR) {
@@ -3590,7 +3591,7 @@ bool modcall_pass2(modcallable *mc)
                        if (!g->vpt && c->name &&
                            (c->name[0] == '&') &&
                            (cf_section_name2_type(g->cs) == T_BARE_WORD)) {
-                               slen = tmpl_afrom_str(g->cs, &g->vpt, c->name,
+                               slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name),
                                                      cf_section_name2_type(g->cs),
                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
                                if (slen < 0) goto parse_error;
@@ -3687,7 +3688,7 @@ bool modcall_pass2(modcallable *mc)
                         *      all of the modules have been loaded.
                         *      Check for that now.
                         */
-                       slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, cf_section_name2_type(g->cs),
+                       slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name), cf_section_name2_type(g->cs),
                                              REQUEST_CURRENT, PAIR_LIST_REQUEST);
                        if (slen < 0) goto parse_error;
 
index 5147f04..a6b2f26 100644 (file)
@@ -134,7 +134,7 @@ next:
 }
 
 
-static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char **out, char const **error, char const *start,
+static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char **out,  char const **error, char const *start,
                                         FR_TOKEN *op)
 {
        char const *p = start;
@@ -169,7 +169,28 @@ static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char **out, char const
 
        while (*p) {
                if (*p == *start) {
-                       *q = '\0';
+                       /*
+                        *      Call the STANDARD parse function to figure out what the string is.
+                        */
+                       if (cf_new_escape) {
+                               ssize_t slen;
+                               value_data_t data;
+                               PW_TYPE src_type = PW_TYPE_STRING;
+
+                               slen = value_data_from_str(ctx, &data, &src_type, NULL, start + 1, p - (start + 1), *start);
+                               if (slen < 0) {
+                                       *error = "error parsing string";
+                                       return slen - 1;
+                               }
+
+                               talloc_free(*out);
+                               *out = talloc_steal(ctx, data.ptr);
+                               data.strvalue = NULL;
+
+                       } else {
+                               *q = '\0'; /* terminate the output string */
+                       }
+
                        p++;
                        return (p - start);
                }
@@ -482,7 +503,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        c->type = COND_TYPE_EXISTS;
                        c->ci = ci;
 
-                       tlen = tmpl_afrom_str(c, &c->data.vpt, lhs, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       tlen = tmpl_afrom_str(c, &c->data.vpt, lhs, talloc_array_length(lhs) - 1, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
                        if (tlen < 0) {
                                p = lhs_p - tlen;
                                return_P(fr_strerror());
@@ -655,7 +676,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                         */
                        c->data.map = map = talloc_zero(c, value_pair_map_t);
 
-                       tlen = tmpl_afrom_str(map, &map->lhs, lhs, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       tlen = tmpl_afrom_str(map, &map->lhs, lhs, talloc_array_length(lhs) - 1, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
                        if (tlen < 0) {
                                p = lhs_p - tlen;
                                return_P(fr_strerror());
@@ -677,7 +698,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                            map_cast_from_hex(map, rhs_type, rhs)) {
                                /* do nothing */
                        } else {
-                               tlen = tmpl_afrom_str(map, &map->rhs, rhs, rhs_type,
+                               tlen = tmpl_afrom_str(map, &map->rhs, rhs, talloc_array_length(rhs) - 1, rhs_type,
                                                      REQUEST_CURRENT, PAIR_LIST_REQUEST);
                                if (tlen < 0) {
                                        p = rhs_p - tlen;
index e26ee2a..882486b 100644 (file)
@@ -935,8 +935,8 @@ finish:
  *     attributes, and instead set type to TMPL_TYPE_ATTR_UNDEFINED.
  * @return <= 0 on error (offset as negative integer), > 0 on success (number of bytes parsed)
  */
-ssize_t tmpl_from_attr_str(value_pair_tmpl_t *vpt, char const *name, request_refs_t request_def,
-                          pair_lists_t list_def, bool allow_undefined)
+ssize_t tmpl_from_attr_str(value_pair_tmpl_t *vpt, char const *name,
+                          request_refs_t request_def, pair_lists_t list_def, bool allow_undefined)
 {
        ssize_t slen;
 
@@ -1201,12 +1201,14 @@ size_t tmpl_prints(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt, D
  * @param[in] list_def The default list to insert unqualified attributes into.
  * @return < 0 on error (offset as negative integer), >= 0 on success (number of bytes parsed)
  */
-ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name, FR_TOKEN type,
+ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name, size_t inlen, FR_TOKEN type,
                       request_refs_t request_def, pair_lists_t list_def)
 {
        char const *p;
        ssize_t slen;
+       PW_TYPE data_type = PW_TYPE_STRING;
        value_pair_tmpl_t *vpt;
+       value_data_t data;
 
        switch (type) {
        case T_BARE_WORD:
@@ -1220,7 +1222,15 @@ ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *nam
                /* FALL-THROUGH */
 
        case T_SINGLE_QUOTED_STRING:
-               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
+               if (cf_new_escape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '\'');
+                       rad_assert(slen >= 0);
+
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
+               } else {
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
+               }
                slen = vpt->len;
                break;
 
@@ -1244,21 +1254,49 @@ ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *nam
                 *      expansion.  Otherwise, convert it to be a
                 *      literal.
                 */
-               if (*p) {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, name, -1);
+               if (cf_new_escape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '"');
+                       rad_assert(slen >= 0);
+
+                       if (*p) {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       } else {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       }
+                       talloc_free(data.ptr);
                } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
+                       if (*p) {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, name, -1);
+                       } else {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
+                       }
                }
                slen = vpt->len;
                break;
 
        case T_BACK_QUOTED_STRING:
-               vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, name, -1);
+               if (cf_new_escape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '`');
+                       rad_assert(slen >= 0);
+
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
+               } else {
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, name, -1);
+               }
                slen = vpt->len;
                break;
 
        case T_OP_REG_EQ: /* hack */
-               vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, name, -1);
+               if (cf_new_escape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '\0'); /* no unescaping */
+                       rad_assert(slen >= 0);
+
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
+               } else {
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, name, -1);
+               }
                slen = vpt->len;
                break;
 
@@ -1277,7 +1315,7 @@ ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *nam
 /** Convert a tmpl containing literal data, to the type specified by da.
  *
  * @param[in,out] vpt the template to modify
- * @param[in] type to case to.
+ * @param[in] type to cast to.
  * @param[in] enumv Enumerated dictionary values.
  * @return true for success, false for failure.
  */
@@ -1295,7 +1333,7 @@ bool tmpl_cast_in_place(value_pair_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *e
        /*
         *      Why do we pass a pointer to the tmpl type? Goddamn WiMAX.
         */
-       ret = value_data_from_str(vpt, &vpt->tmpl_data_value, &vpt->tmpl_data_type, enumv, vpt->name, vpt->len);
+       ret = value_data_from_str(vpt, &vpt->tmpl_data_value, &vpt->tmpl_data_type, enumv, vpt->name, vpt->len, '\0');
        if (ret < 0) return false;
 
        vpt->type = TMPL_TYPE_DATA;
@@ -1329,7 +1367,7 @@ int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
 {
        int rcode;
        VALUE_PAIR *vp;
-       char *str;
+       value_data_t data;
 
        VERIFY_TMPL(vpt);
 
@@ -1346,16 +1384,23 @@ int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
                return 0;
        }
 
-       rcode = radius_expand_tmpl(&str, request, vpt);
+       rcode = radius_expand_tmpl(&data, request, vpt);
        if (rcode < 0) {
                pairfree(&vp);
                return rcode;
        }
 
-       if (pairparsevalue(vp, str, -1) < 0) {
-               talloc_free(str);
-               pairfree(&vp);
-               return rcode;
+       /*
+        *      New escapes: strings are in binary form.
+        */
+       if (cf_new_escape && (vp->da->type == PW_TYPE_STRING)) {
+               vp->data.ptr = talloc_steal(vp, data.ptr);
+               vp->vp_length = rcode;
+
+       } else if (pairparsevalue(vp, data.strvalue, rcode) < 0) {
+                       talloc_free(data.ptr);
+                       pairfree(&vp);
+                       return -1;
        }
 
        *out = vp;
index 1c2daa5..17b01bf 100644 (file)
@@ -490,7 +490,7 @@ static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
 
        switch (vp->da->type) {
        case PW_TYPE_OCTETS:
-               len = fr_print_string((char const *) p, vp->vp_length, out, outlen, '\0');
+               len = fr_print_string((char const *) p, vp->vp_length, out, outlen, '"');
                break;
 
        case PW_TYPE_STRING:
@@ -1922,7 +1922,6 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
 {
        ssize_t rcode;
        char *str = NULL, *child;
-       char *q;
        char const *p;
 
        XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
@@ -2077,28 +2076,44 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                 *
                 *      This is really the reverse of fr_print_string().
                 */
-               p = q = child;
-               while (*p) {
-                       if (*p == '\\') switch (p[1]) {
-                               default:
-                                       *(q++) = p[1];
-                                       p += 2;
-                                       continue;
-
-                               case 'n':
-                                       *(q++) = '\n';
-                                       p += 2;
-                                       continue;
-
-                               case 't':
-                                       *(q++) = '\t';
-                                       p += 2;
-                                       continue;
-                       }
+               if (cf_new_escape && *child) {
+                       ssize_t slen;
+                       PW_TYPE type;
+                       value_data_t data;
+
+                       type = PW_TYPE_STRING;
+                       slen = value_data_from_str(request, &data, &type, NULL, child, talloc_array_length(child) - 1, '"');
+                       rad_assert(slen > 0);
+
+                       talloc_free(child);
+                       child = data.ptr;
 
-                       *(q++) = *(p++);
+               } else {
+                       char *q;
+
+                       p = q = child;
+                       while (*p) {
+                               if (*p == '\\') switch (p[1]) {
+                                       default:
+                                               *(q++) = p[1];
+                                               p += 2;
+                                               continue;
+
+                                       case 'n':
+                                               *(q++) = '\n';
+                                               p += 2;
+                                               continue;
+
+                                       case 't':
+                                               *(q++) = '\t';
+                                               p += 2;
+                                               continue;
+                                       }
+
+                               *(q++) = *(p++);
+                       }
+                       *q = '\0';
                }
-               *q = '\0';
 
                str = talloc_array(ctx, char, 2048); /* FIXME: have the module call talloc_typed_asprintf */
                *str = '\0';    /* Be sure the string is NULL terminated, we now only free on error */
index c9a9cfa..5d0b3bc 100644 (file)
@@ -41,7 +41,7 @@ if (<string>"%{unescape:%{request:Tmp-String-0}}" != &Tmp-String-0) {
        }
 }
 
-if (<string>"%{unescape:%{request:Tmp-String-3}}" != &Tmp-String-1) {
+if (<string>"%{unescape:%{request:Tmp-String-3}}" != "%{Tmp-String-1}") {
        update reply {
                Filter-Id += 'Fail 5'
        }
index ae68ab1..967656d 100644 (file)
@@ -14,12 +14,25 @@ update request {
        Tmp-String-1 += '0x01\0010x07\0070x0A\n0x0D\r""0xb0\260°'
 
        Tmp-String-2 := 'i have scary embedded things\000 inside me'
-       Tmp-String-2 += '0x01\0010x07\0070x0A\n0x0D\r\'\'0xb0\260°'
+       Tmp-String-2 += "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°"
 
        reply:Filter-Id := "filter"
 }
 
-if ("%{string:Tmp-Octets-0}" != 'i have scary embedded things\000 inside me') {
+
+if ("%{length:&Tmp-String-0}" != 39) {
+       update reply {
+               Filter-Id += 'fail l-0'
+       }
+}
+
+if ("%{length:&Tmp-String-1}" != 42) {
+       update reply {
+               Filter-Id += 'fail l-1'
+       }
+}
+
+if ("%{string:Tmp-Octets-0}" != "i have scary embedded things\000 inside me") {
        update reply {
                Filter-Id += 'fail 1'
        }
@@ -31,26 +44,26 @@ if (&Tmp-String-0 != "i have scary embedded things\000 inside me") {
        }
 }
 
-if ("%{string:Tmp-Octets-1}" != '0x01\0010x07\0070x0A\n0x0D\r""0xb0\260°') {
+if ("%{string:Tmp-Octets-1}" != "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") {
        update reply {
                Filter-Id += 'fail 3'
        }
 }
 
-if ("%{Tmp-String-0[0]}" != 'i have scary embedded things\000 inside me') {
+if ("%{Tmp-String-0[0]}" != "i have scary embedded things\000 inside me") {
        update reply {
                Filter-Id += 'fail 4'
        }
 }
 
-if ("%{Tmp-String-0[1]}" != '0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°') {
+if ("%{Tmp-String-0[1]}" != "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") {
        update reply {
                Filter-Id += 'fail 5'
        }
 }
 
 # And another slightly different codepath...
-if ("%{Tmp-String-0[*]}" != 'i have scary embedded things\000 inside me,0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°') {
+if ("%{Tmp-String-0[*]}" != "i have scary embedded things\000 inside me,0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°") {
        update reply {
                Filter-Id += 'fail 6'
        }
@@ -66,7 +79,7 @@ if (&Tmp-String-0[0] != &Tmp-String-0[0]) {
 #  This seems weird... double escapes for most things, but single escapes
 #  for the quotation marks.
 #
-if ("%{Tmp-String-2[1]}" != '0x01\0010x07\0070x0A\n0x0D\r\'\'0xb0\260°') {
+if ("%{Tmp-String-2[1]}" != "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°") {
        update reply {
                Filter-Id += 'fail 8'
        }
@@ -75,7 +88,7 @@ if ("%{Tmp-String-2[1]}" != '0x01\0010x07\0070x0A\n0x0D\r\'\'0xb0\260°') {
 #
 # And again as an attribute reference
 #
-if (&Tmp-String-2[1] != '0x01\0010x07\0070x0A\n0x0D\r\'\'0xb0\260°') {
+if (&Tmp-String-2[1] != "0x01\0010x07\0070x0A\n0x0D\r''0xb0\260°") {
        update reply {
                Filter-Id += 'fail 9'
        }
index 528918f..769e6fd 100644 (file)
@@ -598,3 +598,9 @@ data ERROR offset 1 Invalid list qualifier
 # . is a valid dictionary name attribute, so we can't error out in pass1
 condition &not-a-packet.User-Name == &not-a-packet.User-Name
 data &not-a-packet.User-Name == &not-a-packet.User-Name
+
+#
+#  The LHS is a string with ASCII 5C 30 30 30 inside of it.
+#
+condition ('i have scary embedded things\000 inside me' == "i have scary embedded things\000 inside me")
+data false