You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Ruediger Pluem <rp...@apache.org> on 2024/09/09 09:02:12 UTC

Re: svn commit: r1919325 - in /httpd/httpd/trunk: changes-entries/pr69197.txt modules/mappers/mod_rewrite.c

Any comments on the below?

Regards

RĂ¼diger

On 8/8/24 10:18 AM, Ruediger Pluem wrote:
> 
> 
> On 7/17/24 10:50 PM, ylavic@apache.org wrote:
>> Author: ylavic
>> Date: Wed Jul 17 20:50:12 2024
>> New Revision: 1919325
>>
>> URL: http://svn.apache.org/viewvc?rev=1919325&view=rev
>> Log:
>> mod_rewrite: Better question mark tracking to avoid UnsafeAllow3F.  PR 69197.
>>
>> Track in do_expand() whether a '?' in the uri-path comes from a literal in
>> the substitution string or from an expansion (variable, lookup, ...).
>> In the former case it's safe to assume that it's the query-string separator
>> but for the other case it's not (could be a decoded %3f from r->uri).
>>
>> This allows to avoid [UnsafeAllow3F] for most cases.
>>
>>
>> Added:
>>     httpd/httpd/trunk/changes-entries/pr69197.txt
>> Modified:
>>     httpd/httpd/trunk/modules/mappers/mod_rewrite.c
>>
> 
>>
>> Modified: httpd/httpd/trunk/modules/mappers/mod_rewrite.c
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/mappers/mod_rewrite.c?rev=1919325&r1=1919324&r2=1919325&view=diff
>> ==============================================================================
>> --- httpd/httpd/trunk/modules/mappers/mod_rewrite.c (original)
>> +++ httpd/httpd/trunk/modules/mappers/mod_rewrite.c Wed Jul 17 20:50:12 2024
>> @@ -2402,17 +2402,45 @@ static APR_INLINE char *find_char_in_cur
>>   * of an earlier expansion to include expansion specifiers that
>>   * are interpreted by a later expansion, producing results that
>>   * were not intended by the administrator.
>> + *
>> + * unsafe_qmark if not NULL will be set to 1 or 0 if a question mark
>> + * is found respectively in a literal or in a lookup/expansion (whether
>> + * it's the first or last qmark depends on [QSL]). Should be initialized
>> + * to -1 and remains so if no qmark is found.
>>   */
>> -static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry, apr_pool_t *pool)
>> +static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry,
>> +                       int *unsafe_qmark, apr_pool_t *pool)
>>  {
>> +#define EXPAND_SPECIALS "\\$%"
>>      result_list *result, *current;
>>      result_list sresult[SMALL_EXPANSION];
>>      unsigned spc = 0;
>>      apr_size_t span, inputlen, outlen;
>>      char *p, *c;
>>  
>> -    span = strcspn(input, "\\$%");
>>      inputlen = strlen(input);
>> +    if (!unsafe_qmark) {
>> +        span = strcspn(input, EXPAND_SPECIALS);
>> +    }
>> +    else {
>> +        span = strcspn(input, EXPAND_SPECIALS "?");
>> +        if (input[span] == '?') {
>> +            /* this qmark is not from an expansion thus safe */
>> +            *unsafe_qmark = 0;
>> +
>> +            /* keep tracking only if interested in the last qmark */
>> +            if (entry && (entry->flags & RULEFLAG_QSLAST)) {
>> +                do {
>> +                    span++;
>> +                    span += strcspn(input + span, EXPAND_SPECIALS "?");
>> +                } while (input[span] == '?');
> 
> Why do we need this loop? Isn't that the same as
> 
> span += strcspn(input + span, EXPAND_SPECIALS);
> 
>> +            }
>> +            else {
>> +                unsafe_qmark = NULL;
>> +                span += strcspn(input + span, EXPAND_SPECIALS);
>> +            }
>> +        }
>> +    }
>>  
>>      /* fast exit */
>>      if (inputlen == span) {
> 
>> @@ -2570,8 +2603,41 @@ static char *do_expand(char *input, rewr
>>              ++outlen;
>>          }
>>  
>> +        if (unsafe_qmark && expanded && current->len
>> +            && memchr(current->string, '?', current->len)) {
>> +            /* this qmark is from an expansion thus unsafe */
>> +            *unsafe_qmark = 1;
>> +
>> +            /* keep tracking only if interested in the last qmark */
>> +            if (!entry || !(entry->flags & RULEFLAG_QSLAST)) {
>> +                unsafe_qmark = NULL;
>> +            }
>> +        }
>> +
>>          /* check the remainder */
>> -        if (*p && (span = strcspn(p, "\\$%")) > 0) {
> 
> Can't we just do a break here in the !*p case?
> 
>> +        if (!unsafe_qmark) {
>> +            span = strcspn(p, EXPAND_SPECIALS);
>> +        }
>> +        else {
>> +            span = strcspn(p, EXPAND_SPECIALS "?");
>> +            if (p[span] == '?') {
>> +                /* this qmark is not from an expansion thus safe */
>> +                *unsafe_qmark = 0;
>> +
>> +                /* keep tracking only if interested in the last qmark */
>> +                if (entry && (entry->flags & RULEFLAG_QSLAST)) {
>> +                    do {
>> +                        span++;
>> +                        span += strcspn(p + span, EXPAND_SPECIALS "?");
>> +                    } while (p[span] == '?');
> 
> Why do we need this loop? Isn't that the same as
> 
> span += strcspn(input + span, EXPAND_SPECIALS);
> 
>> +                }
>> +                else {
>> +                    unsafe_qmark = NULL;
>> +                    span += strcspn(p + span, EXPAND_SPECIALS);
>> +                }
>> +            }
>> +        }
>> +        if (span > 0) {
>>              if (current->len) {
>>                  current->next = (spc < SMALL_EXPANSION)
>>                                  ? &(sresult[spc++])
> 
> Regards
> 
> RĂ¼diger
>