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)
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
- 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
------------------------------------------------------------------------------
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
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
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
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;
}
* 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;
}