You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by "William A. Rowe, Jr." <wr...@lnd.com> on 2000/07/28 19:09:56 UTC

[Patch 1.3.13] O(n^2) vulnerability in mod_isapi?

Folks,

  here are the changes from the 2.0 tree (which is -not- complete
just yet) that apply to 1.3...

  It's a nearly identical patch as the one Dean noted should be a
filter... but then again, 1.3.x will never see filters :)  I added
the termarg to indicate which string header processing stopped at.

  I may be misunderstanding the vulnerability that was cited, so
if anyone has further info/comments/suggestions, I'm game.  Please
do so quickly, I'm out of town from Sat evening on through I don't
know just when, so if we like this, collectively, someone else may
have to apply.  The function needs to be added to apachecore.def.

  Consider it proof of concept till someone confirms this was the
source of the O(n^2) attack, and then I'll bash it if I know that's
the concept by noon Saturday.  Otherwise whomever advocates needs
to test it thoroughly.

  If I'm lucky, I get back Wednesday night :(

Bill

Index: include/util_script.h
===================================================================
RCS file: /home/cvs/apache-1.3/src/include/util_script.h,v
retrieving revision 1.36
diff -u -r1.36 util_script.h
--- include/util_script.h	1999/01/08 17:54:40	1.36
+++ include/util_script.h	2000/07/28 16:59:26
@@ -80,6 +80,10 @@
 API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
 				       int (*getsfunc) (char *, int, void *),
 				       void *getsfunc_data);
+API_EXPORT_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, 
+                                                      char *buffer, 
+                                                      const char **termch,
+                                                      int *termarg, ...);
 API_EXPORT(void) ap_send_size(size_t size, request_rec *r);
 API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, char **env,
                           int shellcmd);
Index: main/util_script.c
===================================================================
RCS file: /home/cvs/apache-1.3/src/main/util_script.c,v
retrieving revision 1.151
diff -u -r1.151 util_script.c
--- main/util_script.c	2000/03/03 05:59:11	1.151
+++ main/util_script.c	2000/07/28 16:59:29
@@ -649,6 +649,62 @@
     return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb);
 }
 
+struct vastrs {
+    va_list args;
+    int arg;
+    const char *curpos;
+};
+
+static int getsfunc_STRING(char *w, int len, void *pvastrs)
+{
+    struct vastrs *strs = (struct vastrs*) pvastrs;
+    char *p;
+    int t;
+    
+    if (!strs->curpos || !*strs->curpos) 
+        return 0;
+    p = strchr(strs->curpos, '\n');
+    if (p)
+        ++p;
+    else
+        p = strchr(strs->curpos, '\0');
+    t = p - strs->curpos;
+    if (t > len)
+        t = len;
+    strncpy (w, strs->curpos, t);
+    if (!strs->curpos[t]) {
+        ++strs->arg;
+        strs->curpos = va_arg(strs->args, const char *);
+    }
+    else
+        strs->curpos += t;
+    return t;    
+}
+
+/* ap_scan_script_header_err_strs() accepts additional const char* args...
+ * each is treated as one or more header lines, and the first non-header
+ * character is returned to **termarg, **termch.  (The first optional 
+ * (char*) arg is counted as 0.)
+ */
+API_EXPORT_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, 
+                                                      char *buffer, 
+                                                      const char **termch,
+                                                      int *termarg, ...)
+{
+    struct vastrs strs;
+    int res;
+
+    va_start(strs.args, buffer);
+    strs.arg = 0;
+    strs.curpos = va_arg(strs.args, char*);
+    res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs);
+    if (termch)
+        *termch = strs.curpos;
+    if (termarg)
+        *termarg = strs.arg;
+    va_end(strs.args);
+    return res;
+}
 
 API_EXPORT(void) ap_send_size(size_t size, request_rec *r)
 {
Index: os/win32/mod_isapi.c
===================================================================
RCS file: /home/cvs/apache-1.3/src/os/win32/mod_isapi.c,v
retrieving revision 1.18
diff -u -r1.18 mod_isapi.c
--- os/win32/mod_isapi.c	1999/08/31 15:22:40	1.18
+++ os/win32/mod_isapi.c	2000/07/28 16:59:44
@@ -402,111 +402,23 @@
 	return TRUE;
 
     case HSE_REQ_SEND_RESPONSE_HEADER:
-	r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
-	sscanf(r->status_line, "%d", &r->status);
-	cid->ecb->dwHttpStatusCode = r->status;
-
-	/* Now fill in the HTTP headers, and the rest of it. Ick.
-	 * lpdwDataType contains a string that has headers (in MIME
-	 * format), a blank like, then (possibly) data. We need
-	 * to parse it.
-	 *
-	 * Easy case first:
+	/* Now fill in the HTTP headers, and the rest of it.
+         * lpdwDataType may contain the status, or we respond 200 OK.
+	 * lpdwDataType may contain a string that has formatted headers
+	 * a blank line, then (possibly) data.  Uses the util_script parser.
 	 */
-	if (!lpdwDataType) {
-	    ap_send_http_header(r);
-	    return TRUE;
-	}
-
-	/* Make a copy - don't disturb the original */
-	data = ap_pstrdup(r->pool, (char *)lpdwDataType);
-
-	/* We *should* break before this while loop ends */
-	while (*data) {
-	    char *value, *lf = strchr(data, '\n');
-	    int p;
-
-#ifdef RELAX_HEADER_RULE
-	    if (lf)
-		*lf = '\0';
-#else
-	    if (!lf) { /* Huh? Invalid data, I think */
-		ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-			    "ISA sent invalid headers: %s", r->filename);
-		SetLastError(ERROR);	/* XXX: Find right error */
-		return FALSE;
-	    }
-
-	    /* Get rid of \n and \r */
-	    *lf = '\0';
-#endif
-	    p = strlen(data);
-	    if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
-	    
-	    /* End of headers */
-	    if (*data == '\0') {
-#ifdef RELAX_HEADER_RULE
-		if (lf)
-#endif
-		    data = lf + 1;	/* Reset data */
-		break;
-	    }
-
-	    if (!(value = strchr(data, ':'))) {
-		SetLastError(ERROR);	/* XXX: Find right error */
-		ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-			    "ISA sent invalid headers", r->filename);
-		return FALSE;
-	    }
-
-	    *value++ = '\0';
-	    while (*value && ap_isspace(*value)) ++value;
-
-	    /* Check all the special-case headers. Similar to what
-	     * ap_scan_script_header_err() does (see that function for
-	     * more detail)
-	     */
+	cid->status = ap_scan_script_header_err_strs(r, NULL, &data, NULL,
+                             lpvBuffer ? lpvBuffer : "200 OK",
+                             lpdwDataType ? (char*)lpdwDataType : "\n", NULL);
+        cid->ecb->dwHttpStatusCode = r->status;
 
-	    if (!strcasecmp(data, "Content-Type")) {
-		char *tmp;
-		/* Nuke trailing whitespace */
-		
-		char *endp = value + strlen(value) - 1;
-		while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
-            
-		tmp = ap_pstrdup (r->pool, value);
-		ap_str_tolower(tmp);
-		r->content_type = tmp;
-	    }
-	    else if (!strcasecmp(data, "Content-Length")) {
-		ap_table_set(r->headers_out, data, value);
-	    }
-	    else if (!strcasecmp(data, "Transfer-Encoding")) {
-		ap_table_set(r->headers_out, data, value);
-	    }
-	    else if (!strcasecmp(data, "Set-Cookie")) {
-		ap_table_add(r->err_headers_out, data, value);
-	    }
-	    else {
-		ap_table_merge(r->err_headers_out, data, value);
-	    }
-	  
-	    /* Reset data */
-#ifdef RELAX_HEADER_RULE
-	    if (!lf) {
-		data += p;
-		break;
-	    }
-#endif
-	    data = lf + 1;
-	}
-	
-	/* All the headers should be set now */
+        /* All the headers should be set now */
 
 	ap_send_http_header(r);
 
 	/* Any data left should now be sent directly */
-	ap_rputs(data, r);
+        if (data && *data)
+	    ap_rputs(data, r);
 
 	return TRUE;