COMMAND

    mod_rewrite

SYSTEMS AFFECTED

    Apache

PROBLEM

    The Apache development  list contains a  fix for a  security issue
    that affects previous versions of Apache, including Apache 1.3.12.
    Apache is only  vulnerable if you  use mod_rewrite and  a specific
    case of the directive RewriteRule.  If the result of a RewriteRule
    is a filename that contains regular expression references then  an
    attacker may be able to access any file on the web server.

    Here are some example RewriteRule  directives.  Both of the  first
    two are vulnerable, but the third is not

        RewriteRule    /test/(.*)		/usr/local/data/test-stuff/$1
        RewriteRule    /more-icons/(.*)		/icons/$1
        RewriteRule    /go/(.*)			http://www.apacheweek.com/$1

    To check if you are vulnerable, make a request for

        http://www.example.com/%25{TIME}

    changing the hostname as appropriate and look for a line like:

        [Fri Oct  6 15:33:20 2000] [error] [client 127.0.0.1] File does not exist: /usr/local/apache/docs/20001006153320

    in your error log.  A patched server reports the error as:

        [Fri Oct  6 15:36:14 2000] [error] [client 127.0.0.1] File does not exist: /usr/local/apache/docs/%{TIME}

    The  unpatched  mod_rewrite  expands  the RewriteRule substitution
    string in four phases: RewriteRule regexp match backreferences $N;
    RewriteCond regexp match backreferences %N; environment  variables
    %{VAR};  and  RewriteMap  lookups  ${mapspec}.   The output of one
    phase  may  contain  expansion  specifiers  (from the interpolated
    string) that weren't  in the original  substitution string, so  if
    the interpolated string  comes from the  network then an  attacker
    can  include  expansions  in  the  RewriteRule  that  the   server
    administrator did not intend.

SOLUTION

    The patch is part of the release of Apache 1.3.14.  Until upgrade,
    users should   check their configuration  files and not  use rules
    that map to a filename  such as the first example above.

    Trustix  Secure  Linux  has  released  several updates for Trustix
    Secure Linux  v1.1 and  1.0x.   Users of  the recent  BETA version
    should also install these packages:

        * apache-1.3.12-6tr.i586.rpm
        * apache-devel-1.3.12-6tr.i586.rpm
        * apache-ssl-1.3.12_1.39-8tr.i586.rpm

    For Caldera Linux:

        ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/SRPMS
        - RPMS/apache-1.3.4-5.i386.rpm
        - RPMS/apache-docs-1.3.4-5.i386.rpm
        - SRPMS/apache-1.3.4-5.src.rpm
        ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/SRPMS
        - RPMS/apache-1.3.9-5S.i386.rpm
        - RPMS/apache-devel-1.3.9-5S.i386.rpm
        - RPMS/apache-docs-1.3.9-5S.i386.rpm
        - SRPMS/apache-1.3.9-5S.src.rpm
        ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/SRPMS
        - RPMS/apache-1.3.11-2D.i386.rpm
        - RPMS/apache-devel-1.3.11-2D.i386.rpm
        - RPMS/apache-docs-1.3.11-2D.i386.rpm
        - SRPMS/apache-1.3.11-2D.src.rpm

    For Conectiva Linux:

        ftp://atualizacoes.conectiva.com.br/4.0/SRPMS/apache-1.3.6-16cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/4.0/i386/apache-1.3.6-16cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.0/i386/apache-devel-1.3.6-16cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.0es/SRPMS/apache-1.3.6-16cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/4.0es/i386/apache-1.3.6-16cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.0es/i386/apache-devel-1.3.6-16cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.1/SRPMS/apache-1.3.9-17cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/4.1/i386/apache-1.3.9-17cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.1/i386/apache-devel-1.3.9-17cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.2/SRPMS/apache-1.3.9-17cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/4.2/i386/apache-1.3.9-17cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/4.2/i386/apache-devel-1.3.9-17cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.0/SRPMS/apache-1.3.12-14cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/5.0/i386/apache-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.0/i386/apache-doc-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.0/i386/apache-devel-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.1/SRPMS/apache-1.3.12-14cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/5.1/i386/apache-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.1/i386/apache-doc-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/5.1/i386/apache-devel-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/SRPMS/apache-1.3.12-14cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/apache-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/apache-doc-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/apache-devel-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/SRPMS/apache-1.3.12-14cl.src.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/apache-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/apache-doc-1.3.12-14cl.i386.rpm
        ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/apache-devel-1.3.12-14cl.i386.rpm

    For Linux-Mandrake:

        Linux-Mandrake 6.0: 6.0/RPMS/apache-1.3.6-29mdk.i586.rpm
                            6.0/RPMS/apache-devel-1.3.6-29mdk.i586.rpm
                            6.0/SRPMS/apache-1.3.6-29mdk.src.rpm
        Linux-Mandrake 6.1: 6.1/RPMS/apache-1.3.9-8mdk.i586.rpm
                            6.1/RPMS/apache-devel-1.3.9-8mdk.i586.rpm
                            6.1/SRPMS/apache-1.3.9-8mdk.src.rpm
        Linux-Mandrake 7.0: 7.0/RPMS/apache-1.3.9-18mdk.i586.rpm
                            7.0/RPMS/apache-devel-1.3.9-18mdk.i586.rpm
                            7.0/RPMS/apache-suexec-1.3.9-18mdk.i586.rpm
                            7.0/SRPMS/apache-1.3.9-18mdk.src.rpm
        Linux-Mandrake 7.1: 7.1/RPMS/apache-1.3.12-15mdk.i586.rpm
                            7.1/RPMS/apache-devel-1.3.12-15mdk.i586.rpm
                            7.1/RPMS/apache-suexec-1.3.12-15mdk.i586.rpm
                            7.1/SRPMS/apache-1.3.12-15mdk.src.rpm

    For RedHat:

        ftp://updates.redhat.com/5.2/alpha/apache-1.3.14-2.5.x.alpha.rpm
        ftp://updates.redhat.com/5.2/alpha/apache-devel-1.3.14-2.5.x.alpha.rpm
        ftp://updates.redhat.com/5.2/sparc/apache-1.3.14-2.5.x.sparc.rpm
        ftp://updates.redhat.com/5.2/sparc/apache-devel-1.3.14-2.5.x.sparc.rpm
        ftp://updates.redhat.com/5.2/i386/apache-1.3.14-2.5.x.i386.rpm
        ftp://updates.redhat.com/5.2/i386/apache-devel-1.3.14-2.5.x.i386.rpm
        ftp://updates.redhat.com/5.2/SRPMS/apache-1.3.14-2.5.x.src.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-devel-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-manual-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-devel-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-manual-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/i386/apache-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-devel-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-manual-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/SRPMS/apache-1.3.14-2.6.2.src.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-devel-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-manual-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-devel-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-manual-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/i386/apache-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-devel-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-manual-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/SRPMS/apache-1.3.14-2.6.2.src.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-devel-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/alpha/apache-manual-1.3.14-2.6.2.alpha.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-devel-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/sparc/apache-manual-1.3.14-2.6.2.sparc.rpm
        ftp://updates.redhat.com/6.2/i386/apache-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-devel-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/i386/apache-manual-1.3.14-2.6.2.i386.rpm
        ftp://updates.redhat.com/6.2/SRPMS/apache-1.3.14-2.6.2.src.rpm
        ftp://updates.redhat.com/7.0/i386/apache-1.3.14-3.i386.rpm
        ftp://updates.redhat.com/7.0/i386/apache-devel-1.3.14-3.i386.rpm
        ftp://updates.redhat.com/7.0/i386/apache-manual-1.3.14-3.i386.rpm
        ftp://updates.redhat.com/7.0/SRPMS/apache-1.3.14-3.src.rpm

    Update for  Immunix OS  6.2 (StackGuarded  versions of  the RedHat
    packages) can be found at:

        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/apache-1.3.14-2.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/apache-devel-1.3.14-2.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/apache-manual-1.3.14-2.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/auth_ldap-1.4.0-3_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/mod_perl-1.23-3_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/php-3.0.17-1.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/php-imap-3.0.17-1.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/php-ldap-3.0.17-1.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/php-manual-3.0.17-1.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/php-pgsql-3.0.17-1.6.2_StackGuard.i386.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/SRPMS/apache-1.3.14-2.6.2_StackGuard.src.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/SRPMS/auth_ldap-1.4.0-3_StackGuard.src.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/SRPMS/mod_perl-1.23-3_StackGuard.src.rpm
        http://www.immunix.org:8080/ImmunixOS/6.2/updates/SRPMS/php-3.0.17-1.6.2_StackGuard.src.rpm

    You can alternatively use this patch, which has been committed:

    Index: mod_rewrite.c
    ===================================================================
    RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.c,v
    retrieving revision 1.158
    retrieving revision 1.160
    diff -u -r1.158 -r1.160
    --- mod_rewrite.c	2000/07/14 20:01:55	1.158
    +++ mod_rewrite.c	2000/09/22 20:34:36	1.160
    @@ -1745,7 +1745,6 @@
         char *output;
         const char *vary;
         char newuri[MAX_STRING_LEN];
    -    char env[MAX_STRING_LEN];
         regex_t *regexp;
         regmatch_t regmatch[MAX_NMATCH];
         backrefinfo *briRR = NULL;
    @@ -1913,20 +1912,7 @@
          *  (`RewriteRule <pat> - [E=...]')
          */
         if (strcmp(output, "-") == 0) {
    -        for (i = 0; p->env[i] != NULL; i++) {
    -            /*  1. take the string  */
    -            ap_cpystrn(env, p->env[i], sizeof(env));
    -            /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
    -            expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
    -            /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
    -            expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
    -            /*  4. expand %{...} (i.e. variables) */
    -            expand_variables_inbuffer(r, env, sizeof(env));
    -            /*  5. expand ${...} (RewriteMap lookups)  */
    -            expand_map_lookups(r, env, sizeof(env));
    -            /*  and add the variable to Apache's structures  */
    -            add_env_variable(r, env);
    -        }
    +	do_expand_env(r, p->env, briRR, briRC);
             if (p->forced_mimetype != NULL) {
                 if (perdir == NULL) {
                     /* In the per-server context we can force the MIME-type
    @@ -1961,17 +1947,7 @@
          *  that there is something to replace, so we create the
          *  substitution URL string in `newuri'.
          */
    -    /*  1. take the output string  */
    -    ap_cpystrn(newuri, output, sizeof(newuri));
    -    /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
    -    expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRR, '$');
    -    /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
    -    expand_backref_inbuffer(r->pool, newuri, sizeof(newuri), briRC, '%');
    -    /*  4. expand %{...} (i.e. variables) */
    -    expand_variables_inbuffer(r, newuri, sizeof(newuri));
    -    /*  5. expand ${...} (RewriteMap lookups)  */
    -    expand_map_lookups(r, newuri, sizeof(newuri));
    -    /*  and log the result... */
    +    do_expand(r, output, newuri, sizeof(newuri), briRR, briRC);
         if (perdir == NULL) {
             rewritelog(r, 2, "rewrite %s -> %s", uri, newuri);
         }
    @@ -1983,20 +1959,7 @@
          *  Additionally do expansion for the environment variable
          *  strings (`RewriteRule .. .. [E=<string>]').
          */
    -    for (i = 0; p->env[i] != NULL; i++) {
    -        /*  1. take the string  */
    -        ap_cpystrn(env, p->env[i], sizeof(env));
    -        /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
    -        expand_backref_inbuffer(r->pool, env, sizeof(env), briRR, '$');
    -        /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
    -        expand_backref_inbuffer(r->pool, env, sizeof(env), briRC, '%');
    -        /*  4. expand %{...} (i.e. variables) */
    -        expand_variables_inbuffer(r, env, sizeof(env));
    -        /*  5. expand ${...} (RewriteMap lookups)  */
    -        expand_map_lookups(r, env, sizeof(env));
    -        /*  and add the variable to Apache's structures  */
    -        add_env_variable(r, env);
    -    }
    +    do_expand_env(r, p->env, briRR, briRC);

         /*
          *  Now replace API's knowledge of the current URI:
    @@ -2163,16 +2126,7 @@
          *   Construct the string we match against
          */

    -    /*  1. take the string  */
    -    ap_cpystrn(input, p->input, sizeof(input));
    -    /*  2. expand $N (i.e. backrefs to RewriteRule pattern)  */
    -    expand_backref_inbuffer(r->pool, input, sizeof(input), briRR, '$');
    -    /*  3. expand %N (i.e. backrefs to latest RewriteCond pattern)  */
    -    expand_backref_inbuffer(r->pool, input, sizeof(input), briRC, '%');
    -    /*  4. expand %{...} (i.e. variables) */
    -    expand_variables_inbuffer(r, input, sizeof(input));
    -    /*  5. expand ${...} (RewriteMap lookups)  */
    -    expand_map_lookups(r, input, sizeof(input));
    +    do_expand(r, p->input, input, sizeof(input), briRR, briRC);

         /*
          *   Apply the patterns
    @@ -2314,8 +2268,141 @@
     ** +-------------------------------------------------------+
     */

    +
    +/*
    +**
    +**  perform all the expansions on the input string
    +**  leaving the result in the supplied buffer
    +**
    +*/
    +
    +static void do_expand(request_rec *r, char *input, char *buffer, int nbuf,
    +		       backrefinfo *briRR, backrefinfo *briRC)
    +{
    +    char *inp, *outp;
    +    size_t span, space;
    +
    +    /*
    +     * for security reasons this expansion must be perfomed in a
    +     * single pass, otherwise an attacker can arrange for the result
    +     * of an earlier expansion to include expansion specifiers that
    +     * are interpreted by a later expansion, producing results that
    +     * were not intended by the administrator.
    +     */
    +
    +    inp = input;
    +    outp = buffer;
    +    space = nbuf - 1; /* room for '\0' */
    +
    +    for (;;) {
    +	span = strcspn(inp, "$%");
    +	if (span > space) {
    +	    span = space;
    +	}
    +	memcpy(outp, inp, span);
    +	inp += span;
    +	outp += span;
    +	space -= span;
    +	if (space == 0 || *inp == '\0') {
    +	    break;
    +	}
    +	/* now we have a '$' or a '%' */
    +	if (inp[1] == '{') {
    +	    char *endp;
    +	    endp = strchr(inp, '}');
    +	    if (endp == NULL) {
    +		goto skip;
    +	    }
    +	    *endp = '\0';
    +	    if (inp[0] == '$') {
    +		/* ${...} map lookup expansion */
    +		char *key, *dflt, *result;
    +		key = strchr(inp, ':');
    +		if (key == NULL) {
    +		    goto skip;
    +		}
    +		*key++ = '\0';
    +		dflt = strchr(key, '|');
    +		if (dflt) {
    +		    *dflt++ = '\0';
    +		}
    +		result = lookup_map(r, inp+2, key);
    +		if (result == NULL) {
    +		    result = dflt ? dflt : "";
    +		}
    +		span = ap_cpystrn(outp, result, space) - outp;
    +		key[-1] = ':';
    +		if (dflt) {
    +		    dflt[-1] = '|';
    +		}
    +	    }
    +	    else if (inp[0] == '%') {
    +		/* %{...} variable lookup expansion */
    +		span = ap_cpystrn(outp, lookup_variable(r, inp+2), space) - outp;
    +	    }
    +	    else {
    +		span = 0;
    +	    }
    +	    *endp = '}';
    +	    inp = endp+1;
    +	    outp += span;
    +	    space -= span;
    +	    continue;
    +	}
    +	else if (ap_isdigit(inp[1])) {
    +	    int n = inp[1] - '0';
    +	    backrefinfo *bri = NULL;
    +	    if (inp[0] == '$') {
    +		/* $N RewriteRule regexp backref expansion */
    +		bri = briRR;
    +	    }
    +	    else if (inp[0] == '%') {
    +		/* %N RewriteCond regexp backref expansion */
    +		bri = briRC;
    +	    }
    +	    /* see ap_pregsub() in src/main/util.c */
    +            if (bri && n <= bri->nsub &&
    +		bri->regmatch[n].rm_eo > bri->regmatch[n].rm_so) {
    +		span = bri->regmatch[n].rm_eo - bri->regmatch[n].rm_so;
    +		if (span > space) {
    +		    span = space;
    +		}
    +		memcpy(outp, bri->source + bri->regmatch[n].rm_so, span);
    +		outp += span;
    +		space -= span;
    +	    }
    +	    inp += 2;
    +	    continue;
    +	}
    +    skip:
    +	*outp++ = *inp++;
    +	space--;
    +    }
    +    *outp++ = '\0';
    +}
    +
    +
     /*
     **
    +**  perform all the expansions on the environment variables
    +**
    +*/
    +
    +static void do_expand_env(request_rec *r, char *env[],
    +			  backrefinfo *briRR, backrefinfo *briRC)
    +{
    +    int i;
    +    char buf[MAX_STRING_LEN];
    +
    +    for (i = 0; env[i] != NULL; i++) {
    +	do_expand(r, env[i], buf, sizeof(buf), briRR, briRC);
    +	add_env_variable(r, buf);
    +    }
    +}
    +
    +
    +/*
    +**
     **  split out a QUERY_STRING part from
     **  the current URI string
     **
    @@ -2484,51 +2571,6 @@

     /*
     **
    -**  Expand the %0-%9 or $0-$9 regex backreferences
    -**
    -*/
    -
    -static void expand_backref_inbuffer(pool *p, char *buf, int nbuf,
    -                                    backrefinfo *bri, char c)
    -{
    -    register int i;
    -
    -    /* protect existing $N and & backrefs and replace <c>N with $N backrefs */
    -    for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
    -        if (buf[i] == '\\' && (buf[i+1] != '\0' && i < (nbuf-1))) {
    -            i++; /* protect next */
    -        }
    -        else if (buf[i] == '&') {
    -            buf[i] = '\001';
    -        }
    -        else if (c != '$' && buf[i] == '$' && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
    -            buf[i] = '\002';
    -            i++; /* speedup */
    -        }
    -        else if (buf[i] == c && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
    -            buf[i] = '$';
    -            i++; /* speedup */
    -        }
    -    }
    -
    -    /* now apply the standard regex substitution function */
    -    ap_cpystrn(buf, ap_pregsub(p, buf, bri->source,
    -                               bri->nsub+1, bri->regmatch), nbuf);
    -
    -    /* restore the original $N and & backrefs */
    -    for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
    -        if (buf[i] == '\001') {
    -            buf[i] = '&';
    -        }
    -        else if (buf[i] == '\002') {
    -            buf[i] = '$';
    -        }
    -    }
    -}
    -
    -
    -/*
    -**
     **  Expand tilde-paths (/~user) through
     **  Unix /etc/passwd database information
     **
    @@ -2571,121 +2613,8 @@
     }
     #endif

    -/*
    -**
    -**  mapfile expansion support
    -**  i.e. expansion of MAP lookup directives
    -**  ${<mapname>:<key>} in RewriteRule rhs
    -**
    -*/
    -
    -#define limit_length(n) (n > LONG_STRING_LEN-1 ? LONG_STRING_LEN-1 : n)
    -
    -static void expand_map_lookups(request_rec *r, char *uri, int uri_len)
    -{
    -    char newuri[MAX_STRING_LEN];
    -    char *cpI;
    -    char *cpIE;
    -    char *cpO;
    -    char *cpT;
    -    char *cpT2;
    -    char mapname[LONG_STRING_LEN];
    -    char mapkey[LONG_STRING_LEN];
    -    char defaultvalue[LONG_STRING_LEN];
    -    int n;
    -
    -    cpI = uri;
    -    cpIE = cpI+strlen(cpI);
    -    cpO = newuri;
    -    while (cpI < cpIE) {
    -        if (cpI+6 < cpIE && strncmp(cpI, "${", 2) == 0) {
    -            /* missing delimiter -> take it as plain text */
    -            if (   strchr(cpI+2, ':') == NULL
    -                || strchr(cpI+2, '}') == NULL) {
    -                memcpy(cpO, cpI, 2);
    -                cpO += 2;
    -                cpI += 2;
    -                continue;
    -            }
    -            cpI += 2;
    -
    -            cpT = strchr(cpI, ':');
    -            n = cpT-cpI;
    -            memcpy(mapname, cpI, limit_length(n));
    -            mapname[limit_length(n)] = '\0';
    -            cpI += n+1;
    -
    -            cpT2 = strchr(cpI, '|');
    -            cpT = strchr(cpI, '}');
    -            if (cpT2 != NULL && cpT2 < cpT) {
    -                n = cpT2-cpI;
    -                memcpy(mapkey, cpI, limit_length(n));
    -                mapkey[limit_length(n)] = '\0';
    -                cpI += n+1;
    -
    -                n = cpT-cpI;
    -                memcpy(defaultvalue, cpI, limit_length(n));
    -                defaultvalue[limit_length(n)] = '\0';
    -                cpI += n+1;
    -            }
    -            else {
    -                n = cpT-cpI;
    -                memcpy(mapkey, cpI, limit_length(n));
    -                mapkey[limit_length(n)] = '\0';
    -                cpI += n+1;

    -                defaultvalue[0] = '\0';
    -            }

    -            cpT = lookup_map(r, mapname, mapkey);
    -            if (cpT != NULL) {
    -                n = strlen(cpT);
    -                if (cpO + n >= newuri + sizeof(newuri)) {
    -                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
    -                                 r, "insufficient space in "
    -                                 "expand_map_lookups, aborting");
    -                    return;
    -                }
    -                memcpy(cpO, cpT, n);
    -                cpO += n;
    -            }
    -            else {
    -                n = strlen(defaultvalue);
    -                if (cpO + n >= newuri + sizeof(newuri)) {
    -                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
    -                                 r, "insufficient space in "
    -                                 "expand_map_lookups, aborting");
    -                    return;
    -                }
    -                memcpy(cpO, defaultvalue, n);
    -                cpO += n;
    -            }
    -        }
    -        else {
    -            cpT = strstr(cpI, "${");
    -            if (cpT == NULL)
    -                cpT = cpI+strlen(cpI);
    -            n = cpT-cpI;
    -            if (cpO + n >= newuri + sizeof(newuri)) {
    -                ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
    -                             r, "insufficient space in "
    -                             "expand_map_lookups, aborting");
    -                return;
    -            }
    -            memcpy(cpO, cpI, n);
    -            cpO += n;
    -            cpI += n;
    -        }
    -    }
    -    *cpO = '\0';
    -    ap_cpystrn(uri, newuri, uri_len);
    -    return;
    -}
    -
    -#undef limit_length
    -
    -
    -
     /*
     ** +-------------------------------------------------------+
     ** |                                                       |
    @@ -3478,53 +3407,6 @@
     ** +-------------------------------------------------------+
     */

    -
    -static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len)
    -{
    -    char *newbuf;
    -    newbuf = expand_variables(r, buf);
    -    if (strcmp(newbuf, buf) != 0) {
    -        ap_cpystrn(buf, newbuf, buf_len);
    -    }
    -    return;
    -}
    -
    -static char *expand_variables(request_rec *r, char *str)
    -{
    -    char output[MAX_STRING_LEN];
    -    char input[MAX_STRING_LEN];
    -    char *cp;
    -    char *cp2;
    -    char *cp3;
    -    int expanded;
    -    char *outp;
    -    char *endp;
    -
    -    ap_cpystrn(input, str, sizeof(input));
    -    output[0] = '\0';
    -    outp = output;
    -    endp = output + sizeof(output);
    -    expanded = 0;
    -    for (cp = input; cp < input+MAX_STRING_LEN; ) {
    -        if ((cp2 = strstr(cp, "%{")) != NULL) {
    -            if ((cp3 = strstr(cp2, "}")) != NULL) {
    -                *cp2 = '\0';
    -                outp = ap_cpystrn(outp, cp, endp - outp);
    -
    -                cp2 += 2;
    -                *cp3 = '\0';
    -                outp = ap_cpystrn(outp, lookup_variable(r, cp2), endp - outp);
    -
    -                cp = cp3+1;
    -                expanded = 1;
    -                continue;
    -            }
    -        }
    -        outp = ap_cpystrn(outp, cp, endp - outp);
    -        break;
    -    }
    -    return expanded ? ap_pstrdup(r->pool, output) : str;
    -}

     static char *lookup_variable(request_rec *r, char *var)
     {
    Index: mod_rewrite.h
    ===================================================================
    RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.h,v
    retrieving revision 1.67
    retrieving revision 1.69
    diff -u -r1.67 -r1.69
    --- mod_rewrite.h	2000/09/14 11:54:14	1.67
    +++ mod_rewrite.h	2000/09/22 20:34:36	1.69
    @@ -420,14 +420,16 @@
                                   char *perdir, backrefinfo *briRR,
                                   backrefinfo *briRC);

    +static void do_expand(request_rec *r, char *input, char *buffer, int nbuf,
    +		       backrefinfo *briRR, backrefinfo *briRC);
    +static void do_expand_env(request_rec *r, char *env[],
    +			  backrefinfo *briRR, backrefinfo *briRC);
    +
         /* URI transformation function */
     static void  splitout_queryargs(request_rec *r, int qsappend);
     static void  fully_qualify_uri(request_rec *r);
     static void  reduce_uri(request_rec *r);
    -static void  expand_backref_inbuffer(pool *p, char *buf, int nbuf,
    -                                     backrefinfo *bri, char c);
     static char *expand_tildepaths(request_rec *r, char *uri);
    -static void  expand_map_lookups(request_rec *r, char *uri, int uri_len);

         /* rewrite map support functions */
     static char *lookup_map(request_rec *r, char *name, char *key);
    @@ -466,8 +468,6 @@
     static int   rewritemap_program_child(void *cmd, child_info *pinfo);

         /* env variable support */
    -static void  expand_variables_inbuffer(request_rec *r, char *buf, int buf_len);
    -static char *expand_variables(request_rec *r, char *str);
     static char *lookup_variable(request_rec *r, char *var);
     static char *lookup_header(request_rec *r, const char *name);

    Since the Apache Group released 1.3.14 (which incorporates the
    mod_rewrite security "fix") we have been informed that it breaks
    most configurations that use RewriteMaps, because the lookup key
    is no longer expanded.  Below is a patch to restore the lost
    functionality:

    Index: mod_rewrite.c
    ===================================================================
    RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.c,v
    retrieving revision 1.162
    retrieving revision 1.163
    diff -u -r1.162 -r1.163
    --- mod_rewrite.c	2000/09/22 20:47:19	1.162
    +++ mod_rewrite.c	2000/10/18 04:26:43	1.163
    @@ -2258,30 +2258,50 @@
 	    /* now we have a '$' or a '%' */
 	    if (inp[1] == '{') {
 	        char *endp;
    -	    endp = strchr(inp, '}');
    +	    endp = find_closing_bracket(inp+2, '{', '}');
 	        if (endp == NULL) {
 		    goto skip;
 	        }
 	        *endp = '\0';
 	        if (inp[0] == '$') {
 		    /* ${...} map lookup expansion */
    +		/*
    +		 * To make rewrite maps useful the lookup key and
    +		 * default values must be expanded, so we make
    +		 * recursive calls to do the work. For security
    +		 * reasons we must never expand a string that includes
    +		 * verbatim data from the network. The recursion here
    +		 * isn't a problem because the result of expansion is
    +		 * only passed to lookup_map() so it cannot be
    +		 * re-expanded, only re-looked-up. Another way of
    +		 * looking at it is that the recursion is entirely
    +		 * driven by the syntax of the nested curly brackets.
    +		 */
 		    char *key, *dflt, *result;
    +		char xkey[MAX_STRING_LEN];
    +		char xdflt[MAX_STRING_LEN];
    +		char *empty = "";
 		    key = strchr(inp, ':');
 		    if (key == NULL) {
 		        goto skip;
 		    }
 		    *key++ = '\0';
 		    dflt = strchr(key, '|');
    -		if (dflt) {
    +		if (dflt == NULL) {
    +		    dflt = empty;
    +		}
    +		else {
 		        *dflt++ = '\0';
 		    }
    -		result = lookup_map(r, inp+2, key);
    +		do_expand(r, key,  xkey,  sizeof(xkey),  briRR, briRC);
    +		do_expand(r, dflt, xdflt, sizeof(xdflt), briRR, briRC);
    +		result = lookup_map(r, inp+2, xkey);
 		    if (result == NULL) {
    -		    result = dflt ? dflt : "";
    +		    result = xdflt;
 		    }
 		    span = ap_cpystrn(outp, result, space) - outp;
 		    key[-1] = ':';
    -		if (dflt) {
    +		if (dflt != empty) {
 		        dflt[-1] = '|';
 		    }
 	        }
    @@ -4141,6 +4161,28 @@
             }
         }
         return 0;
    +}
    +
    +/*
    +**
    +**  Find end of bracketed expression
    +**  s points after the opening bracket
    +**
    +*/
    +
    +static char *find_closing_bracket(char *s, int left, int right)
    +{
    +    int depth;
    +
    +    for (depth = 1; *s; ++s) {
    +	if (*s == right && --depth == 0) {
    +	    return s;
    +	}
    +	else if (*s == left) {
    +	    ++depth;
    +	}
    +    }
    +    return NULL;
     }

     /*EOF*/
    Index: mod_rewrite.h
    ===================================================================
    RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.h,v
    retrieving revision 1.72
    retrieving revision 1.73
    diff -u -r1.72 -r1.73
    --- mod_rewrite.h	2000/09/29 17:32:32	1.72
    +++ mod_rewrite.h	2000/10/18 04:26:43	1.73
    @@ -496,6 +496,9 @@
         /* Lexicographic Comparison */
     static int compare_lexicography(char *cpNum1, char *cpNum2);

    +    /* Find end of bracketed expression */
    +static char *find_closing_bracket(char *s, int left, int right);
    +
     #endif /* _MOD_REWRITE_H */

     /*EOF*/

    Hp advised following:

        * The HP Praesidium Proxy  should NOT be used to  serve static
          html content.
        * Static  content  should  be  served  by  the SYSTEM  OUTSIDE
          iPlanet webserver.
        * RewriteRule  directives should  not rewrite  to a  reference
          from the "/" filesystem.