You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by Dean Gaudet <dg...@hyperreal.com> on 1997/06/24 05:03:54 UTC

cvs commit: apache/src CHANGES http_request.c mod_cern_meta.c mod_dir.c mod_negotiation.c

dgaudet     97/06/23 20:03:53

  Modified:    src       CHANGES http_request.c mod_cern_meta.c mod_dir.c 
                        mod_negotiation.c
  Log:
  Fix a few security problems.  Avoid problems with pipes, sockets, etc. in
  the filesystem.  Use sub_req_lookup_file for various functions that
  open ancillary files, so that they have to pass the symlink tests.  Also
  disallow slashes in HeaderName and ReadmeName to avoid ../../../hacks.
  
  Revision  Changes    Path
  1.296     +16 -3     apache/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /export/home/cvs/apache/src/CHANGES,v
  retrieving revision 1.295
  retrieving revision 1.296
  diff -C3 -r1.295 -r1.296
  *** CHANGES	1997/06/24 01:10:56	1.295
  --- CHANGES	1997/06/24 03:03:47	1.296
  ***************
  *** 7,24 ****
      *) Added NT support [Ben Laurie and Ambarish Malpani <am...@valicert.com>]
    
    Changes with Apache 1.2.1
    
      *) Update Unixware support for 2.1.2.  [Lawrence Rosenman <le...@lerctr.org>]
         PR#511
  !   
      *) Port to NonStop-UX [Joachim Schmitz <sc...@tandem.com>] PR#327
  !   
      *) Update ConvexOS support for 11.5.  [David DeSimone <fo...@convex.com>]
         PR#399
    
      *) Support for dec cc compiler under ultrix.
         ["P. Alejandro Lopez-Valencia" <al...@ideam.gov.co>] PR#388
  !   
      *) Support for Maxion/OS SVR4.2 Real Time Unix. [no name given] PR#383
    
      *) mod_status dumps core in inetd mode.  [Marc Slemko and Roy Fielding]
  --- 7,37 ----
      *) Added NT support [Ben Laurie and Ambarish Malpani <am...@valicert.com>]
    
    Changes with Apache 1.2.1
  +   
  +   *) Don't serve file system objects unless they are plain files, symlinks,
  +      or directories.  This prevents local users from using pipes or
  +      named sockets to invoke programs for an extremely crude form of
  +      CGI.  [Dean Gaudet]
  +   
  +   *) HeaderName and ReadmeName were settable in .htaccess and could
  +      contain "../" allowing a local user to "publish" any file on the
  +      system.  No slashes are allowed now.  [Dean Gaudet]
  + 
  +   *) It was possible to violate the symlink Options using mod_dir (headers,
  +      readmes, titles), mod_negotiation (type maps), or mod_cern_meta
  +      (meta files).  [Dean Gaudet]
    
      *) Update Unixware support for 2.1.2.  [Lawrence Rosenman <le...@lerctr.org>]
         PR#511
  ! 
      *) Port to NonStop-UX [Joachim Schmitz <sc...@tandem.com>] PR#327
  ! 
      *) Update ConvexOS support for 11.5.  [David DeSimone <fo...@convex.com>]
         PR#399
    
      *) Support for dec cc compiler under ultrix.
         ["P. Alejandro Lopez-Valencia" <al...@ideam.gov.co>] PR#388
  ! 
      *) Support for Maxion/OS SVR4.2 Real Time Unix. [no name given] PR#383
    
      *) mod_status dumps core in inetd mode.  [Marc Slemko and Roy Fielding]
  
  
  
  1.52      +41 -4     apache/src/http_request.c
  
  Index: http_request.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/http_request.c,v
  retrieving revision 1.51
  retrieving revision 1.52
  diff -C3 -r1.51 -r1.52
  *** http_request.c	1997/06/15 19:22:27	1.51
  --- http_request.c	1997/06/24 03:03:47	1.52
  ***************
  *** 85,90 ****
  --- 85,108 ----
     * they change, all the way down.
     */
    
  + 
  + /*
  +  * We don't want people able to serve up pipes, or unix sockets, or other
  +  * scary things.  Note that symlink tests are performed later.
  +  */
  + static int check_safe_file(request_rec *r)
  + {
  +     if (r->finfo.st_mode == 0		/* doesn't exist */
  + 	|| S_ISDIR (r->finfo.st_mode)
  + 	|| S_ISREG (r->finfo.st_mode)
  + 	|| S_ISLNK (r->finfo.st_mode)) {
  + 	return OK;
  +     }
  +     log_reason("object is not a file, directory or symlink", r->filename, r);
  +     return HTTP_FORBIDDEN;
  + }
  + 
  + 
    int check_symlinks (char *d, int opts)
    { 
    #if defined(__EMX__) || defined(WIN32)
  ***************
  *** 310,320 ****
        if (res != OK) {
    	return res;
        }
  !     
        if (test_filename[strlen(test_filename)-1] == '/')
    	--num_dirs;
    
  !     if (S_ISDIR (r->finfo.st_mode)) ++num_dirs;
    
        for (i = 1; i <= num_dirs; ++i) {
            core_dir_config *core_dir =
  --- 328,344 ----
        if (res != OK) {
    	return res;
        }
  ! 
  !     if ((res = check_safe_file(r))) {
  ! 	return res;
  !     }
  ! 
        if (test_filename[strlen(test_filename)-1] == '/')
    	--num_dirs;
    
  !     if (S_ISDIR (r->finfo.st_mode)) {
  ! 	++num_dirs;
  !     }
    
        for (i = 1; i <= num_dirs; ++i) {
            core_dir_config *core_dir =
  ***************
  *** 399,406 ****
    
        r->per_dir_config = per_dir_defaults;
    
  !     if ((res = check_symlinks (r->filename, allow_options(r))))
  !     {
    	log_reason("Symbolic link not allowed", r->filename, r);
    	return res;
        }
  --- 423,438 ----
    
        r->per_dir_config = per_dir_defaults;
    
  !     /* Symlink permissions are determined by the parent.  If the request is for
  !      * a directory then applying the symlink test here would use the
  !      * permissions of the directory as opposed to its parent.  Consider a
  !      * symlink pointing to a dir with a .htaccess disallowing symlinks.  If you
  !      * access /symlink (or /symlink/) you would get a 403 without this S_ISDIR
  !      * test.  But if you accessed /symlink/index.html, for example, you would
  !      * *not* get the 403.
  !      */
  !     if (!S_ISDIR (r->finfo.st_mode)
  ! 	&& (res = check_symlinks (r->filename, allow_options(r)))) {
    	log_reason("Symbolic link not allowed", r->filename, r);
    	return res;
        }
  ***************
  *** 667,672 ****
  --- 699,709 ----
    	rnew->filename = make_full_path (rnew->pool, fdir, new_file);
    	if (stat (rnew->filename, &rnew->finfo) < 0) {
    	    rnew->finfo.st_mode = 0;
  + 	}
  + 
  + 	if ((res = check_safe_file(rnew))) {
  + 	    rnew->status = res;
  + 	    return rnew;
    	}
    
    	rnew->per_dir_config = r->per_dir_config;
  
  
  
  1.11      +11 -19    apache/src/mod_cern_meta.c
  
  Index: mod_cern_meta.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_cern_meta.c,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -C3 -r1.10 -r1.11
  *** mod_cern_meta.c	1997/03/07 14:15:39	1.10
  --- mod_cern_meta.c	1997/06/24 03:03:48	1.11
  ***************
  *** 131,136 ****
  --- 131,137 ----
    #include <sys/stat.h>
    #include "util_script.h"
    #include "http_log.h"
  + #include "http_request.h"
    
    #define DEFAULT_METADIR		".web"
    #define DEFAULT_METASUFFIX	".meta"
  ***************
  *** 242,247 ****
  --- 243,249 ----
        FILE *f;   
        cern_meta_config *cmc ;
        int rv;
  +     request_rec *rr;
    
        cmc = get_module_config (r->server->module_config,
                               &cern_meta_module); 
  ***************
  *** 276,304 ****
    
        metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL);
    
  !     /*
  !      * stat can legitimately fail for a bewildering number of reasons,
  !      * only one of which implies the file isn't there.  A hardened
  !      * version of this module should test for all conditions, but later...
         */
  !     if (stat(metafilename, &meta_stat) == -1) {
  ! 	/* stat failed, possibly file missing */
    	return DECLINED;
  !     };
  ! 
  !     /*
  !      * this check is to be found in other Jan/96 Apache code, I've
  !      * not been able to find any corroboration in the man pages but
  !      * I've been wrong before so I'll put it in anyway.  Never
  !      * admit to being clueless...
  !      */
  !     if ( meta_stat.st_mode == 0 ) {
  ! 	/* stat failed, definately file missing */
  ! 	return DECLINED;
  !     };
    
        f = pfopen (r->pool, metafilename, "r");
  -     
        if (f == NULL) {
            log_reason("meta file permissions deny server access", metafilename, r);
            return FORBIDDEN;
  --- 278,296 ----
    
        metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL);
    
  !     /* XXX: it sucks to require this subrequest to complete, because this
  !      * means people must leave their meta files accessible to the world.
  !      * A better solution might be a "safe open" feature of pfopen to avoid
  !      * pipes, symlinks, and crap like that.
         */
  !     rr = sub_req_lookup_file (metafilename, r);
  !     if (rr->status != HTTP_OK) {
  ! 	destroy_sub_req (rr);
    	return DECLINED;
  !     }
  !     destroy_sub_req (rr);
    
        f = pfopen (r->pool, metafilename, "r");
        if (f == NULL) {
            log_reason("meta file permissions deny server access", metafilename, r);
            return FORBIDDEN;
  
  
  
  1.30      +19 -0     apache/src/mod_dir.c
  
  Index: mod_dir.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_dir.c,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -C3 -r1.29 -r1.30
  *** mod_dir.c	1997/06/23 11:36:56	1.29
  --- mod_dir.c	1997/06/24 03:03:48	1.30
  ***************
  *** 176,186 ****
  --- 176,192 ----
    }
    
    const char *add_header(cmd_parms *cmd, void *d, char *name) {
  +     if (strchr (name, '/')) {
  + 	return "HeaderName cannot contain a /";
  +     }
        push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
        return NULL;
    }
    
    const char *add_readme(cmd_parms *cmd, void *d, char *name) {
  +     if (strchr (name, '/')) {
  + 	return "ReadmeName cannot contain a /";
  +     }
        push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
        return NULL;
    }
  ***************
  *** 451,457 ****
  --- 457,465 ----
        FILE *f;
        struct stat finfo;
        int plaintext=0;
  +     request_rec *rr;
    
  +     /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */
        fn = make_full_path(r->pool, name, readme_fname);
        fn = pstrcat(r->pool, fn, ".html", NULL);
        if(stat(fn,&finfo) == -1) {
  ***************
  *** 464,469 ****
  --- 472,485 ----
            rputs("<PRE>\n", r);
        }
        else if (rule) rputs("<HR>\n", r);
  +     /* XXX: when the above is rewritten properly, this necessary security
  +      * check will be redundant. -djg */
  +     rr = sub_req_lookup_file (fn, r);
  +     if (rr->status != HTTP_OK) {
  + 	destroy_sub_req (rr);
  + 	return 0;
  +     }
  +     destroy_sub_req (rr);
        if(!(f = pfopen(r->pool,fn,"r")))
            return 0;
        if (!plaintext)
  ***************
  *** 505,510 ****
  --- 521,529 ----
        FILE *thefile = NULL;
        int x,y,n,p;
    
  +     if (r->status != HTTP_OK) {
  + 	return NULL;
  +     }
        if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
            if(!(thefile = pfopen(r->pool, r->filename,"r")))
                return NULL;
  
  
  
  1.43      +9 -6      apache/src/mod_negotiation.c
  
  Index: mod_negotiation.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/mod_negotiation.c,v
  retrieving revision 1.42
  retrieving revision 1.43
  diff -C3 -r1.42 -r1.43
  *** mod_negotiation.c	1997/06/17 00:09:14	1.42
  --- mod_negotiation.c	1997/06/24 03:03:49	1.43
  ***************
  *** 645,661 ****
        return cp;
    }
    
  ! int read_type_map (negotiation_state *neg, char *map_name)
    {
        request_rec *r = neg->r;
  !     FILE *map = pfopen (neg->pool, map_name, "r");
  ! 
        char buffer[MAX_STRING_LEN];
        enum header_state hstate;
        struct var_rec mime_info;
        
        if (map == NULL) {
  !         log_reason("cannot access type map file", map_name, r);
    	return FORBIDDEN;
        }
    
  --- 645,664 ----
        return cp;
    }
    
  ! static int read_type_map (negotiation_state *neg, request_rec *rr)
    {
        request_rec *r = neg->r;
  !     FILE *map;
        char buffer[MAX_STRING_LEN];
        enum header_state hstate;
        struct var_rec mime_info;
        
  +     if (rr->status != HTTP_OK) {
  + 	return rr->status;
  +     }
  +     map = pfopen (neg->pool, rr->filename, "r");
        if (map == NULL) {
  !         log_reason("cannot access type map file", rr->filename, r);
    	return FORBIDDEN;
        }
    
  ***************
  *** 783,789 ****
    	    closedir(dirp);
    	    
    	    neg->avail_vars->nelts = 0;
  ! 	    return read_type_map (neg, sub_req->filename);
    	}
    	
    	/* Have reasonable variant --- gather notes.
  --- 786,792 ----
    	    closedir(dirp);
    	    
    	    neg->avail_vars->nelts = 0;
  ! 	    return read_type_map (neg, sub_req);
    	}
    	
    	/* Have reasonable variant --- gather notes.
  ***************
  *** 1853,1859 ****
        
        char *udir;
        
  !     if ((res = read_type_map (neg, r->filename))) return res;
        
        maybe_add_default_encodings(neg, 0);
        
  --- 1856,1862 ----
        
        char *udir;
        
  !     if ((res = read_type_map (neg, r))) return res;
        
        maybe_add_default_encodings(neg, 0);