Return-Path: Delivered-To: apmail-apache-cvs-archive@apache.org Received: (qmail 32151 invoked by uid 500); 22 Sep 2000 20:34:38 -0000 Mailing-List: contact apache-cvs-help@apache.org; run by ezmlm Precedence: bulk Reply-To: new-httpd@apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list apache-cvs@apache.org Received: (qmail 32140 invoked by uid 500); 22 Sep 2000 20:34:37 -0000 Delivered-To: apmail-apache-1.3-cvs@apache.org Date: 22 Sep 2000 20:34:36 -0000 Message-ID: <20000922203436.32135.qmail@locus.apache.org> From: fanf@locus.apache.org To: apache-1.3-cvs@apache.org Subject: cvs commit: apache-1.3/src/modules/standard mod_rewrite.c mod_rewrite.h fanf 00/09/22 13:34:36 Modified: src/modules/standard mod_rewrite.c mod_rewrite.h Log: Fix a security problem that affects certain configurations of mod_rewrite. If the result of a RewriteRule is a filename that contains expansion specifiers, especially regexp backreferences $0..$9 and %0..%9, then it may be possible for an attacker to access any file on the web server. The fix replaces a multi-pass string expander with a one-pass expander. Message-Id: Revision Changes Path 1.160 +100 -215 apache-1.3/src/modules/standard/mod_rewrite.c Index: mod_rewrite.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.c,v retrieving revision 1.159 retrieving revision 1.160 diff -u -u -r1.159 -r1.160 --- mod_rewrite.c 2000/09/22 20:23:15 1.159 +++ mod_rewrite.c 2000/09/22 20:34:36 1.160 @@ -2279,16 +2279,106 @@ static void do_expand(request_rec *r, char *input, char *buffer, int nbuf, backrefinfo *briRR, backrefinfo *briRC) { - /* 1. take the string */ - ap_cpystrn(buffer, input, nbuf); - /* 2. expand $N (i.e. backrefs to RewriteRule pattern) */ - expand_backref_inbuffer(r->pool, buffer, nbuf, briRR, '$'); - /* 3. expand %N (i.e. backrefs to latest RewriteCond pattern) */ - expand_backref_inbuffer(r->pool, buffer, nbuf, briRC, '%'); - /* 4. expand %{...} (i.e. variables) */ - expand_variables_inbuffer(r, buffer, nbuf); - /* 5. expand ${...} (RewriteMap lookups) */ - expand_map_lookups(r, buffer, nbuf); + 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'; } @@ -2481,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 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 ** @@ -2568,121 +2613,8 @@ } #endif -/* -** -** mapfile expansion support -** i.e. expansion of MAP lookup directives -** ${:} 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 - - - /* ** +-------------------------------------------------------+ ** | | @@ -3475,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) { 1.69 +1 -6 apache-1.3/src/modules/standard/mod_rewrite.h Index: mod_rewrite.h =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_rewrite.h,v retrieving revision 1.68 retrieving revision 1.69 diff -u -u -r1.68 -r1.69 --- mod_rewrite.h 2000/09/22 20:23:15 1.68 +++ mod_rewrite.h 2000/09/22 20:34:36 1.69 @@ -168,7 +168,7 @@ #include #endif #endif -#if defined(AIX) || defined(AIXIA64) +#ifdef AIX #undef USE_FLOCK #define USE_FCNTL 1 #include @@ -429,10 +429,7 @@ 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); @@ -471,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);