A string sample cannot contain a \0 byte by design: this is what
distinguishes it from a binary sample, and every consumer relies on it.
Yet url_dec decoded "%00" to a \0, json_query decoded "\u0000" to one,
and jwt_header_query/jwt_payload_query could return one from the base64
decoded JSON. Such a sample was then matched by "-m str" on its prefix
only, so "/public%00/admin" matched "/public" and an allow-list could be
bypassed.
Commit
442f583f4 ("BUG/MINOR: pattern: do not match a string with an
embedded \0 on its prefix") worked around it in pat_match_str(), which
is the wrong place since such a string must not exist at all.
This commit reverts it and fixes the producers instead: url_decode() now
fails on "%00" like it does on an invalid sequence, and json_query and
jwt_*_query fail on a decoded \0. This is also cheaper, as the string
is checked once where it is produced instead of on every call to
pat_match_str().
This must be backported to all stable branches, in place of
442f583f4
where it was not yet backported.
struct pattern *ret = NULL;
struct lru64 *lru = NULL;
- /* Patterns never contain a NUL byte, so a sample with an embedded
- * NUL cannot be an exact match. Without this check, the tree lookup
- * would stop at the NUL and match on the prefix only.
- */
- if (memchr(smp->data.u.str.area, 0, smp->data.u.str.data))
- return NULL;
-
/* Lookup a string in the expression's pattern tree. */
if (!eb_is_empty(&expr->pattern_tree)) {
if (!pat_match_ensure_str(smp))
return 0;
}
+ /* mjson might return an embedded \0 that is not valid as a string */
+ if (memchr(trash->area, 0, len))
+ return 0;
+
trash->data = len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
if (ret == -1)
goto end;
+ /* mjson might return an embedded \0 that is not valid as a string */
+ if (memchr(decoded_header->area, 0, ret))
+ goto end;
+
decoded_header->data = ret;
if (args[0].type != ARGT_STR) {
smp->data.u.str = *decoded_header;
case '%' :
if (!ishex(in[1]) || !ishex(in[2]))
goto end;
- *out++ = (hex2i(in[1]) << 4) + hex2i(in[2]);
+ *out = (hex2i(in[1]) << 4) + hex2i(in[2]);
+ /* forbid %00 which cannot be represented in a string */
+ if (!*out)
+ goto end;
+ out++;
in += 2;
break;
case '?':