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