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 <[EMAIL PROTECTED]>] Changes with Apache 1.2.1 *) Update Unixware support for 2.1.2. [Lawrence Rosenman <ler@lerctr.org>] PR#511 ! *) Port to NonStop-UX [Joachim Schmitz <[EMAIL PROTECTED]>] PR#327 ! *) Update ConvexOS support for 11.5. [David DeSimone <[EMAIL PROTECTED]>] PR#399 *) Support for dec cc compiler under ultrix. ["P. Alejandro Lopez-Valencia" <[EMAIL PROTECTED]>] 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 <[EMAIL PROTECTED]>] 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 <ler@lerctr.org>] PR#511 ! *) Port to NonStop-UX [Joachim Schmitz <[EMAIL PROTECTED]>] PR#327 ! *) Update ConvexOS support for 11.5. [David DeSimone <[EMAIL PROTECTED]>] PR#399 *) Support for dec cc compiler under ultrix. ["P. Alejandro Lopez-Valencia" <[EMAIL PROTECTED]>] 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);