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: <[EMAIL PROTECTED]>
  -                and <[EMAIL PROTECTED]>
  -        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
  
  
  

Reply via email to