]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
*) Correct a vulnerability in the Win32 and OS2 ports, by which a
authorWilliam A. Rowe Jr <wrowe@apache.org>
Thu, 10 May 2001 04:07:57 +0000 (04:07 +0000)
committerWilliam A. Rowe Jr <wrowe@apache.org>
Thu, 10 May 2001 04:07:57 +0000 (04:07 +0000)
     client submitting a carefully constructed URI could cause a GP
     (segment) fault in the child process, which would have to be
     cleared by the operator to resume operation.  This vulnerability
     introduced no identified means to comprimize the server's data.
     Reported by Auriemma Luigi <kaino3@genie.it>.
     [William Rowe, Brian Harvard]

PR:  7522

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@89065 13f79535-47bb-0310-9956-ffa450edef68

STATUS
src/CHANGES
src/os/os2/util_os2.c
src/os/win32/util_win32.c

diff --git a/STATUS b/STATUS
index fb799c2abae9a3e55a4989c7cdcb0e3072e5f284..1ef4cd43f21fe74d431a504698f9e9e1bde96621 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -1,9 +1,10 @@
 APACHE 1.3 STATUS:                                             -*-text-*-
-  Last modified at [$Date: 2001/04/02 09:22:02 $]
+  Last modified at [$Date: 2001/05/10 04:07:54 $]
 
 Release:
 
-   1.3.20-dev: Current version.
+   1.3.20: In development - security exploit demands a release ASAP.
+           Will offers to RM, tag and roll 5/10 9:00pm PST.
    1.3.19: Tagged and rolled Feb 26, 2001. Announced Mar 01, 2001.
    1.3.18: Not released.
              (Pulled because of an incorrect unescaping fix. t/r Feb 19, 2001)
@@ -255,7 +256,8 @@ Open issues:
       the filename (think win32, or samba-mounted filesystems).  There are
       several PR's to this and I don't see for security reasons why we can't
       accomodate it, though it does add complexity to suexec.c.
-      Accepting quoted executable names solves that issue.
+      Accepting quoted executable names solves that issue, except that the
+      exec cmd="" parsing needs to accept escaped quotes.
       PR #1120
       Brian: +1
       
@@ -303,24 +305,15 @@ Win32 specific issues:
        - mod_log_referer.c
        - mod_mime_magic.c (needs access to mod_mime API stage...)
 
-    * do something to disable bogus warnings
+    * do something to disable bogus warnings ... Will asks "Which warnings?"
 
     * rfc1413.c has static storage which won't work multithreaded
 
-    * mod_include --> exec cgi, exec cmd, etc. don't work right.
-      Looks like a code path that isn't run anywhere else that has
-      something not quite right...  A PR or two on it.
-
-    * Currently if you double click on the conf files or the
-      log files you get a useless dialog offering the set of all
-      executables, usually after a very long pause.  Ought
-      to stuff .conf in the registry mapping it to text.
-
     * apparently either "BrowserMatch" or the "nokeepalive" variable
       cause instability - see PR#1729.
 
 
-Binaries (1.3.19):
+Binaries (1.3.20):
 
  Platform                      Avail.  Volunteer
  ------------------------------------------------------------------------------
@@ -350,7 +343,7 @@ Binaries (1.3.19):
  i386-unknown-netBSD-1.3.2     no      Lars Eilebrecht, Randy Terbush
  i386-unknown-sco3             no      Ben Laurie
  i386-unknown-sco5             no      Ben Laurie
- i386-siemens-sinix5.4         yes     Martin Kraemer
+ i386-siemens-sinix5.4         no      Martin Kraemer
  i386-dg-dgux5.4R2.01          no      Randy Terbush
  x86-qnx-4.x                   no      Randy Terbush
  x86-bsdos-3.x                 no      Randy Terbush
@@ -364,9 +357,9 @@ Binaries (1.3.19):
  mips-sgi-irix5.3              no      Mark Imbrianco, Randy Terbush
  mips-sgi-irix6.2              no      Lars Eilebrecht, Randy Terbush
  mips-sgi-irix6.4              no      Lars Eilebrecht
- mips-siemens-reliantunix5.4   yes     Martin Kraemer
- netware                       yes     Brad Nicholes <bnicholes@novell.com>
- OS/2                          yes     Brian Havard
+ mips-siemens-reliantunix5.4   no      Martin Kraemer
+ netware                       no      Brad Nicholes <bnicholes@novell.com>
+ OS/2                          no      Brian Havard
  OS/390-09.00-02               no                     
  powerpc+i386-apple-darwin1.2  no      Wilfredo Sanchez
  powerpc-apple-rhapsody5.5     no      Wilfredo Sanchez
@@ -379,4 +372,4 @@ Binaries (1.3.19):
  sparc-sun-solaris2.7          no      Cliff Skolnick
  sparc-sun-sunos4.1.3_U1       no      Sameer Parekh
  sparc-unknown-linux           no      Lars Eilebrecht, Randy Terbush
- win32                         yes     William Rowe
+ win32                         no      William Rowe
index 6a1262ebc7e918e632f0fa0eba154105d8ef1258..ab40f99b06c0873740ffbb7633056789e3695bc5 100644 (file)
@@ -1,5 +1,13 @@
 Changes with Apache 1.3.20
 
+  *) Correct a vulnerability in the Win32 and OS2 ports, by which a 
+     client submitting a carefully constructed URI could cause a GP
+     (segment) fault in the child process, which would have to be
+     cleared by the operator to resume operation.  This vulnerability
+     introduced no identified means to comprimize the server's data.
+     Reported by Auriemma Luigi <kaino3@genie.it>.
+     [William Rowe, Brian Harvard]
+
   *) Resolve the Win32 SSI exec cmd bug, where cmd was not executed
      appropriately against the shell.  [William Rowe]
 
index 2a77a4ad5cad870cecb0b9f2d09f204296775535..be2fff632534eb2b1e23f02d11029a72dd0a7304 100644 (file)
@@ -7,14 +7,14 @@
 
 API_EXPORT(char *)ap_os_case_canonical_filename(pool *pPool, const char *szFile)
 {
-    char buf[HUGE_STRING_LEN];
-    char buf2[HUGE_STRING_LEN];
+    char *buf;
+    char buf2[CCHMAXPATH];
     int rc, len; 
     char *pos;
     
 /* Remove trailing slash unless it's a root directory */
-    strcpy(buf, szFile);
-    len = strlen(buf);
+    len = strlen(szFile);
+    buf = ap_pstrndup(pPool, szFile, len);
     
     if (len > 3 && buf[len-1] == '/')
         buf[--len] = 0;
@@ -26,7 +26,7 @@ API_EXPORT(char *)ap_os_case_canonical_filename(pool *pPool, const char *szFile)
         }
     }
 
-    rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN);
+    rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, sizeof(buf2));
 
     if (rc) {
         if ( rc != ERROR_INVALID_NAME ) {
index 591fccfee8d97ceac783164f405b16f60548bd5f..980f6b41bc6fb8a96fabe2c5c3eda8581e780735 100644 (file)
@@ -83,140 +83,185 @@ static BOOL OnlyDots(char *pString)
     return TRUE;
 }
 
+
 /* Accepts as input a pathname, and tries to match it to an 
  * existing path and return the pathname in the case that
  * is present on the existing path.  This routine also
  * converts alias names to long names.
+ *
+ * WARNING: Folding to systemcase fails when /path/to/foo/../bar
+ * is given and foo does not exist, is not a directory.
  */
 API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, 
                                              const char *szFile)
 {
-    char buf[HUGE_STRING_LEN];
-    char *pInputName;
-    char *p, *q, *t;
+    char *buf, *t, *r;
+    const char *q, *p;
     BOOL bDone = FALSE;
     BOOL bFileExists = TRUE;
     HANDLE hFind;
     WIN32_FIND_DATA wfd;
+    size_t buflen;
+    int slack = 0;
 
-    if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf))
+    if (!szFile || strlen(szFile) == 0)
         return ap_pstrdup(pPool, "");
 
-    t = buf;
-    pInputName = ap_pstrdup(pPool, szFile);
+    buflen = strlen(szFile);
+    t = buf = ap_palloc(pPool, buflen + 1);
+    q = szFile;
 
-    /* First convert all slashes to \ so Win32 calls work OK */
-    for (p = pInputName; *p; p++) {
-        if (*p == '/')
-            *p = '\\';
-    }
-    
-    q = p = pInputName;
     /* If there is drive information, copy it over. */ 
-    if (pInputName[1] == ':') {
-        /* This is correct - if systemcase is used for
+    if (szFile[1] == ':') {
+        /* Lowercase, so that when systemcase is used for
          * comparison, d: designations will match
-         */                    
-        *(t++) = tolower(*p++);
-        *(t++) = *p++;
-        q = p;
-
-        /* If all we have is a drive letter, then we are done */
-        if (!*p)
-            bDone = TRUE;
-    }    
-
-    if (*p == '\\') {
-        ++p;
-        if (*p == '\\')  /* UNC name */
+         */                 
+        *(t++) = tolower(*(q++));
+        *(t++) = *(q++);
+    }
+    else if ((*q == '/') || (*q == '\\')) {
+        /* Get past the root path (/ or //foo/bar/) so we can go
+         * on to normalize individual path elements.
+         */
+        *(t++) = '\\', ++q;
+        if ((*q == '/') || (*q == '\\'))  /* UNC name */
         {
-            /* Get past the machine name.  FindFirstFile */
-            /* will not find a machine name only */
-            *(t++) = '\\';
-            ++q;
-            p = strchr(p + 1, '\\'); 
-            if (p)
+                /* Lower-case the machine name, so compares match.
+                 * FindFirstFile won't parse \\machine alone
+                 */
+            *(t++) = '\\', ++q;
+            for (p = q; *p && (*p != '/') && (*p != '\\'); ++p)
+                /* continue */ ;
+            if (*p || p > q) 
             {
-                p++;
-                /* Get past the share name.  FindFirstFile */
-                /* will not find a \\machine\share name only */
-                p = strchr(p, '\\'); 
-                if (p) {
-                    /* This was faulty - as of 1.3.13 \\machine\share 
-                     * name is now always lowercased
-                     */
-                    strncpy(t,q,p-q);
-                    strlwr(t);
-                    t += p - q;
-                    q = p;
-                    p++;
+                /* Lower-case the machine name, so compares match.
+                 * FindFirstFile won't parse \\machine\share alone
+                 */
+                memcpy(t, q, p - q);
+                t[p - q] = '\0';
+                strlwr(t);
+                t += p - q;
+                q = p;
+                if (*p) {
+                    *(t++) = '\\', ++q;
+                    for (p = q; *p && (*p != '/') && (*p != '\\'); ++p)
+                        /* continue */ ;
+                    if (*p || p > q) 
+                    {
+                        /* Copy the lower-cased share name.  FindFirstFile 
+                         * cannot not find a \\machine\share name only 
+                         */
+                        memcpy(t, q, p - q);
+                        t[p - q] = '\0';
+                        strlwr(t);
+                        t += p - q;
+                        q = p;
+                        if (*p)
+                            *(t++) = '\\', ++q;
+                        else
+                            bFileExists = FALSE;
+                    }
+                    else
+                        bFileExists = FALSE;
                 }
+                else
+                    bFileExists = FALSE;
             }
-
-            if (!p) {
+            else
                 bFileExists = FALSE;
-                p = q;
-            }
         }
     }
 
-    p = strchr(p, '\\');
+    while (bFileExists) {
 
-    while (!bDone) {
-        if (p)
-            *p = '\0';
+        /* parse past any leading slashes */
+        for (; (*q == '/') || (*q == '\\'); ++q)
+            *(t++) = '\\';
 
-        if (strchr(q, '*') || strchr(q, '?'))
-            bFileExists = FALSE;
+        /* break on end of string */
+        if (!*q)
+            break;
+
+        /* get to the end of this path segment */
+        for (p = q; *p && (*p != '/') && (*p != '\\'); ++p)
+            /* continue */ ;
+                
+        /* copy the segment */
+        memcpy(t, q, p - q);
+        t[p - q] = '\0';
+
+        /* Test for nasties that can exhibit undesired effects */
+        if (strpbrk(t, "?\"<>*|:")) {
+            t += p - q;
+            q = p;
+            break;
+        }
 
         /* If the path exists so far, call FindFirstFile
          * again.  However, if this portion of the path contains
          * only '.' charaters, skip the call to FindFirstFile
          * since it will convert '.' and '..' to actual names.
-         * Note: in the call to OnlyDots, we may have to skip
-         *       a leading slash.
+         * On win32, '...' is an alias for '..', so we gain 
+         * a bit of slack.
          */
-        if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) {            
-            hFind = FindFirstFile(pInputName, &wfd);
-            
-            if (hFind == INVALID_HANDLE_VALUE) {
-                bFileExists = FALSE;
+        if (*t == '.' && OnlyDots(t)) {
+            if (p - q == 3) {
+                t += 2;
+                q = p;
+                ++slack;
             }
             else {
-                FindClose(hFind);
-
-                if (*q == '\\')
-                    *(t++) = '\\';
-                t = strchr(strcpy(t, wfd.cFileName), '\0');
+                t += p - q;
+                q = p;
             }
-        }
-        
-        if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) {
-            /* XXX: Comparison could be faulty ...\unknown
-             * names may not match!
-             */
-            strcpy(t, q);
-            t = strchr(t, '\0');
-        }
-        
-        if (p) {
-            q = p;
-            *p++ = '\\';
-            p = strchr(p, '\\');
+            /* Paths of 4 dots or more are invalid */
+            if (p - q > 3)
+                break;
         }
         else {
-            bDone = TRUE;
+            if ((hFind = FindFirstFile(buf, &wfd)) == INVALID_HANDLE_VALUE) {
+                t += p - q;
+                q = p;
+                break;
+            }
+            else {
+                size_t fnlen = strlen(wfd.cFileName);
+                FindClose(hFind);
+                /* the string length just changed, could have shrunk
+                 * (trailing spaces or dots) or could have grown 
+                 * (longer filename aliases).  Realloc as necessary
+                 */
+                slack -= fnlen - (p - q);
+                if (slack < 0) {
+                    char *n;
+                    slack += buflen + fnlen - (p - q);
+                    buflen += buflen + fnlen - (p - q);
+                    n = ap_palloc(pPool, buflen + 1);
+                    memcpy (n, buf, t - buf);
+                    t = n + (t - buf);
+                    buf = n;
+                }
+                memcpy(t, wfd.cFileName, fnlen);
+                t += fnlen;
+                q = p;
+                if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                    break;
+            }
         }
     }
-    *t = '\0';
-    
-    /* Finally, convert all slashes to / so server code handles it ok */
-    for (p = buf; *p; p++) {
-        if (*p == '\\')
-            *p = '/';
+
+    /* Convert all parsed '\'s to '/' for canonical form (doesn't touch
+     * the non-existant portion of the path whatsoever.)
+     */
+    for (r = buf; r < t; ++r) {
+        if (*r == '\\')
+            *r = '/';
     }
 
-    return ap_pstrdup(pPool, buf);
+    /* Copy the non-existant portion (minimally nul-terminates the string) */
+    strcpy(t, q);
+    
+    return buf;
 }
 
 
@@ -293,56 +338,15 @@ API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool,
      *  simply truncated, with no embedded '~'.  Further, this behavior
      *  can be modified on WinNT volumes.  This was not a safe test,
      *  therefore exclude the '~' pretest.
-     */     
+     */
 #ifdef WIN32_SHORT_FILENAME_INSECURE_BEHAVIOR
      p = strchr(pNewStr, '~');
      if (p != NULL)
 #endif
-     {
-        char *pConvertedName, *pQstr, *pPstr;
-        char buf[HUGE_STRING_LEN];
-        /* We potentially have a short name.  Call 
-         * ap_os_systemcase_filename to examine the filesystem
-         * and possibly extract the long name.
-         */
-        pConvertedName = ap_os_systemcase_filename(pPool, pNewStr);
-
-        /* Since we want to preserve the incoming case as much
-         * as we can, compare for differences in the string and
-         * only substitute in the path names that changed.
-         */
-        if (stricmp(pNewStr, pConvertedName)) {
-            buf[0] = '\0';
-
-            q = pQstr = pConvertedName;
-            p = pPstr = pNewStr;
-            do {
-                q = strchr(q,'/');
-                p = strchr(p,'/');
-
-                if (p != NULL) {
-                    *q = '\0';
-                    *p = '\0';
-                }
-
-                if (stricmp(pQstr, pPstr)) 
-                    strcat(buf, pQstr);   /* Converted name */
-                else 
-                    strcat(buf, pPstr);   /* Original name  */
-
-
-                if (p != NULL) {
-                    pQstr = q;
-                    pPstr = p;
-                    *q++ = '/';
-                    *p++ = '/';
-                }
-
-            } while (p != NULL); 
-
-            pNewStr = ap_pstrdup(pPool, buf);
-        }
-    }
+    /* ap_os_systemcase_filename now changes the case of only
+     * the pathname elements that are found.
+     */
+        pNewStr = ap_os_systemcase_filename(pPool, pNewStr);
 
     return pNewStr;
 }