diff -ruN -x Makefile.in -x configure -x *~ apache_1.3.9.orig/src/modules/standard/mod_rewrite.c apache_1.3.9/src/modules/standard/mod_rewrite.c
--- apache_1.3.9.orig/src/modules/standard/mod_rewrite.c	Fri Jan 26 00:04:22 2001
+++ apache_1.3.9/src/modules/standard/mod_rewrite.c	Fri Jan 26 00:03:13 2001
@@ -1733,7 +1733,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;
@@ -1901,20 +1900,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
@@ -1949,17 +1935,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);
     }
@@ -1971,20 +1947,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:
@@ -2145,16 +2108,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
@@ -2298,6 +2252,159 @@
 
 /*
 **
+**  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 = 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) {
+		    *endp = '}';
+		    goto skip;
+		}
+		*key++ = '\0';
+		dflt = strchr(key, '|');
+		if (dflt == NULL) {
+		    dflt = empty;
+		}
+		else {
+		    *dflt++ = '\0';
+		}
+		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 = xdflt;
+		}
+		span = ap_cpystrn(outp, result, space) - outp;
+		key[-1] = ':';
+		if (dflt != empty) {
+		    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
 **
@@ -2463,48 +2570,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)
-{
-    int i;
-
-    if (bri->nsub < 1) {
-        return;
-    }
-
-    if (c != '$') {
-        /* safe existing $N backrefs and replace <c>N with $N backrefs */
-        for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
-            if (buf[i] == '$' && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
-                buf[i++] = '\001';
-            }
-            else if (buf[i] == c && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
-                buf[i++] = '$';
-            }
-        }
-    }
-
-    /* now apply the pregsub() function */
-    ap_cpystrn(buf, ap_pregsub(p, buf, bri->source,
-                         bri->nsub+1, bri->regmatch), nbuf);
-
-    if (c != '$') {
-        /* restore the original $N backrefs */
-        for (i = 0; buf[i] != '\0' && i < nbuf; i++) {
-            if (buf[i] == '\001' && (buf[i+1] >= '0' && buf[i+1] <= '9')) {
-                buf[i++] = '$';
-            }
-        }
-    }
-}
-
-
-/*
-**
 **  Expand tilde-paths (/~user) through
 **  Unix /etc/passwd database information
 **
@@ -2547,120 +2612,6 @@
 }
 #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
-
-
 
 /*
 ** +-------------------------------------------------------+
@@ -3459,53 +3410,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)
 {
     const char *result;
@@ -4266,6 +4170,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;
 }
 
 
diff -ruN -x Makefile.in -x configure -x *~ apache_1.3.9.orig/src/modules/standard/mod_rewrite.h apache_1.3.9/src/modules/standard/mod_rewrite.h
--- apache_1.3.9.orig/src/modules/standard/mod_rewrite.h	Tue Aug  3 11:27:35 1999
+++ apache_1.3.9/src/modules/standard/mod_rewrite.h	Fri Jan 26 00:04:05 2001
@@ -418,14 +418,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);
@@ -464,8 +466,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);
 
@@ -491,6 +491,7 @@
 
     /* Lexicographic Comparison */
 static int compare_lexicography(char *cpNum1, char *cpNum2);
+static char *find_closing_bracket(char *s, int left, int right);
 
 #endif /* _MOD_REWRITE_H */
 
