Return-Path: Delivered-To: apache-cvs-archive@hyperreal.org Received: (qmail 13671 invoked by uid 6000); 5 Jan 1999 08:17:43 -0000 Received: (qmail 13513 invoked by alias); 5 Jan 1999 08:17:40 -0000 Delivered-To: apache-1.3-cvs@hyperreal.org Received: (qmail 13422 invoked by uid 82); 5 Jan 1999 08:17:38 -0000 Date: 5 Jan 1999 08:17:38 -0000 Message-ID: <19990105081738.13417.qmail@hyperreal.org> From: fielding@hyperreal.org To: apache-1.3-cvs@hyperreal.org Subject: cvs commit: apache-1.3/src/support httpd.exp Sender: apache-cvs-owner@apache.org Precedence: bulk Reply-To: new-httpd@apache.org fielding 99/01/05 00:17:38 Modified: . STATUS src ApacheCore.def CHANGES src/include ap_mmn.h httpd.h src/main http_request.c util.c src/os/win32 util_win32.c src/support httpd.exp Log: ap_os_is_filename_valid() has been added to Win32 to detect and prevent access to special DOS device file names. Submitted by: Paul Sutton, Ken Parzygnat Reviewed by: Roy Fielding Revision Changes Path 1.592 +1 -7 apache-1.3/STATUS Index: STATUS =================================================================== RCS file: /home/cvs/apache-1.3/STATUS,v retrieving revision 1.591 retrieving revision 1.592 diff -u -r1.591 -r1.592 --- STATUS 1999/01/05 07:08:59 1.591 +++ STATUS 1999/01/05 08:17:24 1.592 @@ -1,5 +1,5 @@ 1.3 STATUS: - Last modified at [$Date: 1999/01/05 07:08:59 $] + Last modified at [$Date: 1999/01/05 08:17:24 $] Release: @@ -16,12 +16,6 @@ RELEASE SHOWSTOPPERS: - * Paul's [PATCH] Win32 device files - Message-ID: - and <000401be2225$861f11b0$bf4d2509@wolfpad.raleigh.ibm.com> - Status: Someone who knows the current status of this thing should - apply it now, since the rest of us can't test it otherwise. - * long pathnames with many components and no AllowOverride None Status: Marc is looking at it 1.6 +1 -0 apache-1.3/src/ApacheCore.def Index: ApacheCore.def =================================================================== RCS file: /home/cvs/apache-1.3/src/ApacheCore.def,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- ApacheCore.def 1999/01/03 12:04:34 1.5 +++ ApacheCore.def 1999/01/05 08:17:25 1.6 @@ -323,4 +323,5 @@ ap_single_module_init @316 ap_make_etag @317 ap_array_pstrcat @318 + ap_os_is_filename_valid @319 1.1204 +4 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1203 retrieving revision 1.1204 diff -u -r1.1203 -r1.1204 --- CHANGES 1999/01/05 07:09:01 1.1203 +++ CHANGES 1999/01/05 08:17:25 1.1204 @@ -1,5 +1,9 @@ Changes with Apache 1.3.4 + *) SECURITY: ap_os_is_filename_valid() has been added to Win32 + to detect and prevent access to special DOS device file names. + [Paul Sutton, Ken Parzygnat] + *) WIN32: Created new makefiles Makefile_win32.txt (normal build) and Makefile_win32_debug.txt (debug build) that work on Win95. Run each of the following from the src directory: 1.19 +2 -1 apache-1.3/src/include/ap_mmn.h Index: ap_mmn.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/ap_mmn.h,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- ap_mmn.h 1999/01/03 12:04:36 1.18 +++ ap_mmn.h 1999/01/05 08:17:27 1.19 @@ -195,12 +195,13 @@ * 19990101 - renamed macro escape_uri() to ap_escape_uri() * - added MODULE_MAGIC_COOKIE to identify module structs * 19990103 (1.3.4-dev) - added ap_array_pstrcat() + * 19990105 (1.3.4-dev) - added ap_os_is_filename_valid() to Win32 */ #define MODULE_MAGIC_COOKIE 0x41503133UL /* "AP13" */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 19990103 +#define MODULE_MAGIC_NUMBER_MAJOR 19990105 #endif #define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */ #define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ 1.258 +1 -0 apache-1.3/src/include/httpd.h Index: httpd.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.257 retrieving revision 1.258 diff -u -r1.257 -r1.258 --- httpd.h 1999/01/03 13:35:32 1.257 +++ httpd.h 1999/01/05 08:17:27 1.258 @@ -1025,6 +1025,7 @@ #ifdef WIN32 API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool, const char *szFile); API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, const char *szFile); +API_EXPORT(int) ap_os_is_filename_valid(const char *file); #else #define ap_os_case_canonical_filename(p,f) ap_os_canonical_filename(p,f) #define ap_os_systemcase_filename(p,f) ap_os_canonical_filename(p,f) 1.141 +42 -13 apache-1.3/src/main/http_request.c Index: http_request.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_request.c,v retrieving revision 1.140 retrieving revision 1.141 diff -u -r1.140 -r1.141 --- http_request.c 1999/01/01 19:04:50 1.140 +++ http_request.c 1999/01/05 08:17:29 1.141 @@ -179,7 +179,6 @@ char *last_cp = NULL; int rv; #ifdef WIN32 - char buf[5]; BOOL bStripSlash=TRUE; #endif @@ -189,16 +188,12 @@ } #ifdef WIN32 - /* If the path is x:/, then convert it to x:/., coz that's what stat - * needs to work properly + /* If the directory is x:\, then we don't want to strip + * the trailing slash since x: is not a valid directory. */ - if (strlen(path) == 3 && path[1] == ':') { - strcpy(buf,path); - buf[3]='.'; - buf[4]='\0'; - path=buf; - end=buf+4; - } + if (strlen(path) == 3 && path[1] == ':' && path[2] == '/') + bStripSlash = FALSE; + /* If UNC name == //machine/share/, do not * advance over the trailing slash. Any other @@ -234,8 +229,25 @@ *cp = '\0'; +#ifdef WIN32 + /* We must not stat() filenames such as "/file/aux" since it can cause + * delays or lockups. So pretend that they do not exist by returning + * an ENOENT error. This will force us to drop that part of the path and + * keep looking back for a "real" file that exists, while still allowing + * the "invalid" path parts within the PATH_INFO. + */ + if (!ap_os_is_filename_valid(path)) { + errno = ENOENT; + rv = -1; + } + else { + errno = 0; + rv = stat(path, &r->finfo); + } +#else errno = 0; rv = stat(path, &r->finfo); +#endif if (cp != end) *cp = '/'; @@ -315,7 +327,7 @@ char *test_filename; char *test_dirname; int res; - unsigned i, num_dirs; + unsigned i, num_dirs, iStart; int j, test_filename_len; /* @@ -395,6 +407,14 @@ ap_no2slash(test_filename); num_dirs = ap_count_dirs(test_filename); +#ifdef WIN32 + if (!ap_os_is_filename_valid(r->filename)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Filename is not valid: %s", r->filename); + return HTTP_FORBIDDEN; + } +#endif + if ((res = check_safe_file(r))) { return res; } @@ -415,9 +435,18 @@ */ test_dirname = ap_palloc(r->pool, test_filename_len + 2); + iStart = 1; +#ifdef WIN32 + /* If the name is a UNC name, then do not walk through the + * machine and share name (e.g. \\machine\share\) + */ + if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/') + iStart = 4; +#endif + /* j keeps track of which section we're on, see core_reorder_directories */ j = 0; - for (i = 1; i <= num_dirs; ++i) { + for (i = iStart; i <= num_dirs; ++i) { int overrides_here; core_dir_config *core_dir = (core_dir_config *) ap_get_module_config(per_dir_defaults, &core_module); @@ -803,7 +832,7 @@ rnew->uri = ap_make_full_path(rnew->pool, udir, new_file); rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file); - ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */ + ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */ if (stat(rnew->filename, &rnew->finfo) < 0) { rnew->finfo.st_mode = 0; } 1.144 +10 -0 apache-1.3/src/main/util.c Index: util.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/util.c,v retrieving revision 1.143 retrieving revision 1.144 diff -u -r1.143 -r1.144 --- util.c 1999/01/01 19:04:53 1.143 +++ util.c 1999/01/05 08:17:30 1.144 @@ -761,6 +761,16 @@ return NULL; } +#if defined(WIN32) + if (!ap_os_is_filename_valid(name)) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Access to config file %s denied: not a valid filename", + name); + errno = EACCES; + return NULL; + } +#endif + file = ap_pfopen(p, name, "r"); #ifdef DEBUG saved_errno = errno; 1.31 +140 -0 apache-1.3/src/os/win32/util_win32.c Index: util_win32.c =================================================================== RCS file: /home/cvs/apache-1.3/src/os/win32/util_win32.c,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- util_win32.c 1998/11/06 14:25:58 1.30 +++ util_win32.c 1999/01/05 08:17:32 1.31 @@ -534,3 +534,143 @@ } return return_value; } + +/* + * ap_os_is_filename_valid is given a filename, and returns 0 if the filename + * is not valid for use on this system. On Windows, this means it fails any + * of the tests below. Otherwise returns 1. + * + * Test for filename validity on Win32. This is of tests come in part from + * the MSDN article at "Technical Articles, Windows Platform, Base Services, + * Guidelines, Making Room for Long Filenames" although the information + * in MSDN about filename testing is incomplete or conflicting. There is a + * similar set of tests in "Technical Articles, Windows Platform, Base Services, + * Guidelines, Moving Unix Applications to Windows NT". + * + * The tests are: + * + * 1) total path length greater than MAX_PATH + * + * 2) anything using the octets 0-31 or characters " < > | : + * (these are reserved for Windows use in filenames. In addition + * each file system has its own additional characters that are + * invalid. See KB article Q100108 for more details). + * + * 3) anything ending in "." (no matter how many) + * (filename doc, doc. and doc... all refer to the same file) + * + * 4) any segment in which the basename (before first period) matches + * one of the DOS device names + * (the list comes from KB article Q100108 although some people + * reports that additional names such as "COM5" are also special + * devices). + * + * If the path fails ANY of these tests, the result must be to deny access. + */ + +API_EXPORT(int) ap_os_is_filename_valid(const char *file) +{ + const char *segstart; + char seglength; + const char *pos; + static const char * const invalid_characters = "?\"<>*|:"; + static const char * const invalid_filenames[] = { + "CON", "AUX", "COM1", "COM2", "COM3", + "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL", NULL + }; + + /* Test 1 */ + if (strlen(file) > MAX_PATH) { + /* Path too long for Windows. Note that this test is not valid + * if the path starts with //?/ or \\?\. */ + return 0; + } + + pos = file; + + /* Skip any leading non-path components. This can be either a + * drive letter such as C:, or a UNC path such as \\SERVER\SHARE\. + * We continue and check the rest of the path based on the rules above. + * This means we could eliminate valid filenames from servers which + * are not running NT (such as Samba). + */ + + if (pos[0] && pos[1] == ':') { + /* Skip leading drive letter */ + pos += 2; + } + else { + if ((pos[0] == '\\' || pos[0] == '/') && + (pos[1] == '\\' || pos[1] == '/')) { + /* Is a UNC, so skip the server name and share name */ + pos += 2; + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No share name */ + return 0; + } + pos++; /* Move to start of share name */ + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No path information */ + return 0; + } + } + } + + while (*pos) { + int idx; + int baselength; + + while (*pos == '/' || *pos == '\\') { + pos++; + } + if (*pos == '\0') { + break; + } + segstart = pos; /* start of segment */ + while (*pos && *pos != '/' && *pos != '\\') { + pos++; + } + seglength = pos - segstart; + /* + * Now we have a segment of the path, starting at position "segstart" + * and length "seglength" + */ + + /* Test 2 */ + for (idx = 0; idx < seglength; idx++) { + if (segstart[idx] < 32 || + strchr(invalid_characters, segstart[idx])) { + return 0; + } + } + + /* Test 3 */ + if (segstart[seglength-1] == '.') { + return 0; + } + + /* Test 4 */ + for (baselength = 0; baselength < seglength; baselength++) { + if (segstart[baselength] == '.') { + break; + } + } + + /* baselength is the number of characters in the base path of + * the segment (which could be the same as the whole segment length, + * if it does not include any dot characters). */ + if (baselength == 3 || baselength == 4) { + for (idx = 0; invalid_filenames[idx]; idx++) { + if (!strnicmp(invalid_filenames[idx], segstart, baselength)) { + return 0; + } + } + } + } + + return 1; +} 1.10 +1 -0 apache-1.3/src/support/httpd.exp Index: httpd.exp =================================================================== RCS file: /home/cvs/apache-1.3/src/support/httpd.exp,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- httpd.exp 1999/01/03 12:04:40 1.9 +++ httpd.exp 1999/01/05 08:17:36 1.10 @@ -203,6 +203,7 @@ ap_open_logs ap_open_piped_log ap_os_escape_path +ap_os_is_filename_valid ap_os_is_path_absolute ap_overlay_tables ap_overlap_tables