You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Randy Terbush <ra...@zyzzyva.com> on 1996/07/11 06:14:08 UTC

Centralized exec()

The following patch provides a centralized exec function call_exec().
Changed files include mod_include.c, mod_cgi.c, util.c and a new
file, http_exec.c.  I've added some code to http_exec.c that
should set some sane limits to prevent runaway CGI processes.
These setrlimit() calls are mainly intended for discussion only.
I'm not even sure that this is the logical place to do this.
Comments welcome. I've also added the call to set_child_check_entry()
which is a new security tool that RST has added to the apache-XX.
It's currently commented out until that code is officially brought
into 1.2.

Getting these changes into the source tree will make it easier
for Jason and I to continue pursuit of a secure way to do setuid().
I have another set of patches that should allow us to setuid on
a per virtualhost basis, and I think Jason has some code ready
to handle a per UserDir configuration as well.

Please comment.

Index: src/Makefile.tmpl
===================================================================
RCS file: /export/home/cvs/apache/src/Makefile.tmpl,v
retrieving revision 1.14
diff -c -r1.14 Makefile.tmpl
*** Makefile.tmpl	1996/06/14 16:24:26	1.14
--- Makefile.tmpl	1996/07/11 03:51:16
***************
*** 3,9 ****
  # This is combined with the information in the "Configuration" file
  # by the configure script to make the actual Makefile.
  
! OBJS= alloc.o http_main.o http_core.o http_config.o http_request.o \
    http_log.o http_protocol.o rfc1413.o util.o util_script.o modules.o buff.o\
    md5c.o util_md5.o explain.o http_bprintf.o $(MODULES)
  
--- 3,9 ----
  # This is combined with the information in the "Configuration" file
  # by the configure script to make the actual Makefile.
  
! OBJS= alloc.o http_main.o http_core.o http_config.o http_request.o http_exec.o \
    http_log.o http_protocol.o rfc1413.o util.o util_script.o modules.o buff.o\
    md5c.o util_md5.o explain.o http_bprintf.o $(MODULES)
  
Index: src/httpd.h
===================================================================
RCS file: /export/home/cvs/apache/src/httpd.h,v
retrieving revision 1.37
diff -c -r1.37 httpd.h
*** httpd.h	1996/07/03 22:58:45	1.37
--- httpd.h	1996/07/11 03:53:03
***************
*** 501,506 ****
--- 501,518 ----
  /* more stuff here, like which protocol is bound to the port */
  };
  
+ /* data passed around in the exec() process */
+ typedef struct child_data_struct child_data;
+ 
+ struct child_data_struct {
+     char *cmdstr;
+     int nph;
+     int retval;
+     long magic_offset;
+     request_rec *r;
+ };
+ 
+ 
  /* Prototypes for utilities... util.c.
   */
  
***************
*** 551,557 ****
  uid_t uname2id(char *name);
  gid_t gname2id(char *name);
  int is_directory(char *name);
! int can_exec(struct stat *);     
  void chdir_file(char *file);
       
  char *get_local_host(pool *);
--- 563,570 ----
  uid_t uname2id(char *name);
  gid_t gname2id(char *name);
  int is_directory(char *name);
! void can_exec(child_data *data);     
! void call_exec(child_data *data, int shellcmd);     
  void chdir_file(char *file);
       
  char *get_local_host(pool *);
Index: src/mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.10
diff -c -r1.10 mod_cgi.c
*** mod_cgi.c	1996/06/17 20:43:47	1.10
--- mod_cgi.c	1996/07/11 03:53:49
***************
*** 92,108 ****
   */
  
  
- struct cgi_child_stuff {
-     request_rec *r;
-     int nph;
-     char *argv0;
- };
- 
  void cgi_child (void *child_stuff)
  {
!     struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff;
      request_rec *r = cld->r;
-     char *argv0 = cld->argv0;
      int nph = cld->nph;
  
  #ifdef DEBUG_CGI    
--- 92,101 ----
   */
  
  
  void cgi_child (void *child_stuff)
  {
!     child_data *cld = (child_data *)child_stuff;
      request_rec *r = cld->r;
      int nph = cld->nph;
  
  #ifdef DEBUG_CGI    
***************
*** 115,131 ****
      int i;
  #endif
      
-     char **env;
      char err_string[HUGE_STRING_LEN];
      
  #ifdef DEBUG_CGI    
      fprintf (dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
  	    r->filename, nph ? "NPH " : "", argv0);
  #endif    
  
-     add_cgi_vars (r);
-     env = create_environment (r->pool, r->subprocess_env);
-     
  #ifdef DEBUG_CGI    
      fprintf (dbg, "Environment: \n");
      for (i = 0; env[i]; ++i) fprintf (dbg, "'%s'\n", env[i]);
--- 108,121 ----
      int i;
  #endif
      
      char err_string[HUGE_STRING_LEN];
      
  #ifdef DEBUG_CGI    
+     char *argv0 = cld->cmdstr;
      fprintf (dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
  	    r->filename, nph ? "NPH " : "", argv0);
  #endif    
  
  #ifdef DEBUG_CGI    
      fprintf (dbg, "Environment: \n");
      for (i = 0; env[i]; ++i) fprintf (dbg, "'%s'\n", env[i]);
***************
*** 144,191 ****
      
      cleanup_for_exec();
      
! #ifdef __EMX__    
!     if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) {
!             int emxloop;
!             char *emxtemp;
! 
!             /* For OS/2 place the variables in the current
!             enviornment then it will be inherited. This way
!             the program will also get all of OS/2's other SETs. */
!             for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
!                 putenv(emxtemp);
!                 
!             if (strstr(strupr(r->filename), ".CMD") > 0) {
!                 /* Special case to allow use of REXX commands as scripts. */
!                 os2pathname(r->filename);
!                 execl("CMD.EXE", "CMD.EXE", "/C", r->filename, NULL);
!             } else {
!                 execl(r->filename, argv0, NULL);
!             }
!     } else {
!             int emxloop;
!             char *emxtemp;
!             
!             /* For OS/2 place the variables in the current
!             enviornment then it will be inherited. This way
!             the program will also get all of OS/2's other SETs. */
!             for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
!                 putenv(emxtemp);
!                 
!             if (strstr(strupr(r->filename), ".CMD") > 0) {
!                 /* Special case to allow use of REXX commands as scripts. */
!                 os2pathname(r->filename);
!                 execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, r->filename));
!             } else {
!                 execv(r->filename, create_argv(r->pool, argv0, r->args));
!             }
!     }
! #else
!     if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) 
!         execle(r->filename, argv0, NULL, env);
!     else 
!         execve(r->filename, create_argv(r->pool, argv0, r->args), env);
! #endif        
  
      /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
       * EARTH-shattering kaboom!
--- 134,140 ----
      
      cleanup_for_exec();
      
!     call_exec(cld, 0);
  
      /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
       * EARTH-shattering kaboom!
***************
*** 197,202 ****
--- 146,155 ----
       * was tied to in cleanup_for_exec().  It's only available on stderr
       * now, so that's what we use).
       */
+ 
+     /* If we have a defined error, return to spawn_child */
+     if (cld->retval)
+ 	return;
      
      sprintf(err_string,
  	    "exec of %s failed, errno is %d\n", r->filename, errno);
***************
*** 210,256 ****
      char *argv0;
      FILE *script_out, *script_in;
      char argsbuffer[HUGE_STRING_LEN];
-     int is_included = !strcmp (r->protocol, "INCLUDED");
      char *lenp = table_get (r->headers_in, "Content-length");
  
!     struct cgi_child_stuff cld;
! 
      if((argv0 = strrchr(r->filename,'/')) != NULL)
          argv0++;
      else argv0 = r->filename;
  
      nph = !(strncmp(argv0,"nph-",4));
      
!     if (!(allow_options (r) & OPT_EXECCGI) && !is_scriptaliased (r)) {
!         log_reason("Options ExecCGI is off in this directory", r->filename, r);
! 	return FORBIDDEN;
!     }
!     if (nph && is_included) {
!         log_reason("attempt to include NPH CGI script", r->filename, r);
! 	return FORBIDDEN;
!     }
      
-     if (S_ISDIR(r->finfo.st_mode)) {
-         log_reason("attempt to invoke directory as script", r->filename, r);
- 	return FORBIDDEN;
-     }
-     if (r->finfo.st_mode == 0) {
-         log_reason("script not found or unable to stat", r->filename, r);
- 	return NOT_FOUND;
-     }
-     if(!can_exec(&r->finfo)) {
-         log_reason("file permissions deny server execution", r->filename, r);
-         return FORBIDDEN;
-     }
-     if ((r->method_number == M_POST || r->method_number == M_PUT)
- 	&& !lenp) {
-         log_reason("POST or PUT without Content-length:", r->filename, r);
- 	return BAD_REQUEST;
-     }
- 
      add_common_vars (r);
!     cld.argv0 = argv0; cld.r = r; cld.nph = nph;
!     
  #ifdef __EMX__
      if (r->method_number == M_POST || r->method_number == M_PUT) {
          int len_to_read = atoi (lenp);
--- 163,185 ----
      char *argv0;
      FILE *script_out, *script_in;
      char argsbuffer[HUGE_STRING_LEN];
      char *lenp = table_get (r->headers_in, "Content-length");
+     child_data cld;
  
!     
      if((argv0 = strrchr(r->filename,'/')) != NULL)
          argv0++;
      else argv0 = r->filename;
  
      nph = !(strncmp(argv0,"nph-",4));
      
!     cld.cmdstr = argv0;
!     cld.r = r;
!     cld.nph = nph;
!     cld.retval = 0;
      
      add_common_vars (r);
! 
  #ifdef __EMX__
      if (r->method_number == M_POST || r->method_number == M_PUT) {
          int len_to_read = atoi (lenp);
***************
*** 338,347 ****
      if (script_in && !nph) {
          char *location;
  	int ret;
!       
          if ((ret = scan_script_header(r, script_in)))
  	    return ret;
! 	
  	location = table_get (r->headers_out, "Location");
  
          if (location && location[0] == '/' && r->status == 200) {
--- 267,276 ----
      if (script_in && !nph) {
          char *location;
  	int ret;
! 
          if ((ret = scan_script_header(r, script_in)))
  	    return ret;
! 
  	location = table_get (r->headers_out, "Location");
  
          if (location && location[0] == '/' && r->status == 200) {
Index: src/mod_include.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_include.c,v
retrieving revision 1.9
diff -c -r1.9 mod_include.c
*** mod_include.c	1996/06/07 21:15:33	1.9
--- mod_include.c	1996/07/11 03:54:29
***************
*** 429,443 ****
      }
  }
  
- typedef struct {
-     request_rec *r;
-     char *s;
- } include_cmd_arg;
  
! void include_cmd_child (void *arg)
  {
!     request_rec *r =  ((include_cmd_arg *)arg)->r;
!     char *s = ((include_cmd_arg *)arg)->s;
      table *env = r->subprocess_env;
  #ifdef DEBUG_INCLUDE_CMD    
  #ifdef __EMX__
--- 429,439 ----
      }
  }
  
  
! void include_cmd_child (void *child_stuff)
  {
!     child_data *arg = (child_data *)child_stuff;
!     request_rec *r =  arg->r;
      table *env = r->subprocess_env;
  #ifdef DEBUG_INCLUDE_CMD    
  #ifdef __EMX__
***************
*** 450,456 ****
      char err_string [MAX_STRING_LEN];
  
  #ifdef DEBUG_INCLUDE_CMD    
!     fprintf (dbg, "Attempting to include command '%s'\n", s);
  #endif    
  
      if (r->path_info && r->path_info[0] != '\0')
--- 446,452 ----
      char err_string [MAX_STRING_LEN];
  
  #ifdef DEBUG_INCLUDE_CMD    
!     fprintf (dbg, "Attempting to include command '%s'\n", arg->cmdstr);
  #endif    
  
      if (r->path_info && r->path_info[0] != '\0')
***************
*** 476,490 ****
      error_log2stderr (r->server);
      
  #ifdef DEBUG_INCLUDE_CMD    
!     fprintf (dbg, "Attempting to exec '%s'\n", s);
  #endif    
!     cleanup_for_exec();
!     execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL,
! 	   create_environment (r->pool, env));
      
      /* Oh, drat.  We're still here.  The log file descriptors are closed,
       * so we have to whimper a complaint onto stderr...
       */
      
  #ifdef DEBUG_INCLUDE_CMD    
      fprintf (dbg, "Exec failed\n");
--- 472,489 ----
      error_log2stderr (r->server);
      
  #ifdef DEBUG_INCLUDE_CMD    
!     fprintf (dbg, "Attempting to exec '%s'\n", arg->cmdstr);
  #endif    
!     cleanup_for_exec(); 
!     call_exec(arg, 1);		/* set shellcmd flag to pass arg to SHELL_PATH */
      
      /* Oh, drat.  We're still here.  The log file descriptors are closed,
       * so we have to whimper a complaint onto stderr...
       */
+ 
+     /* If we have a defined error, return to spawn_child */
+     if (arg->retval)
+ 	return;
      
  #ifdef DEBUG_INCLUDE_CMD    
      fprintf (dbg, "Exec failed\n");
***************
*** 495,505 ****
      exit(0);
  }
  
! int include_cmd(char *s, request_rec *r) {
!     include_cmd_arg arg;
      FILE *f;
  
!     arg.r = r; arg.s = s;
  
      if (!spawn_child (r->connection->pool, include_cmd_child, &arg,
  		      kill_after_timeout, NULL, &f))
--- 494,507 ----
      exit(0);
  }
  
! int include_cmd(char *s, request_rec *r)
! {
!     child_data arg;
      FILE *f;
  
!     arg.r = r;
!     arg.cmdstr = s;
!     arg.retval = 0;
  
      if (!spawn_child (r->connection->pool, include_cmd_child, &arg,
  		      kill_after_timeout, NULL, &f))
Index: src/util.c
===================================================================
RCS file: /export/home/cvs/apache/src/util.c,v
retrieving revision 1.13
diff -c -r1.13 util.c
*** util.c	1996/07/09 21:40:14	1.13
--- util.c	1996/07/11 03:55:58
***************
*** 795,815 ****
      else return 0;
  }
  
- int can_exec(struct stat *finfo) {
- #ifdef __EMX__
-     /* OS/2 dosen't have Users and Groups */
-     return (finfo->st_mode & S_IEXEC);
- #else    
-     if(user_id == finfo->st_uid)
-         if(finfo->st_mode & S_IXUSR)
-             return 1;
-     if(group_id == finfo->st_gid)
-         if(finfo->st_mode & S_IXGRP)
-             return 1;
-     return (finfo->st_mode & S_IXOTH);
- #endif    
- }
- 
  #ifdef NEED_STRDUP
  char *strdup (char *str)
  {
--- 795,800 ----
Index: src/util_script.h
===================================================================
RCS file: /export/home/cvs/apache/src/util_script.h,v
retrieving revision 1.3
diff -c -r1.3 util_script.h
*** util_script.h	1996/03/01 02:50:55	1.3
--- util_script.h	1996/07/11 03:56:06
***************
*** 51,57 ****
   *
   */
  
- 
  char **create_argv(pool *p, char *av0, char *args);
  #ifdef __EMX__
  char **create_argv_cmd(pool *p, char *av0, char *args, char *path);
--- 51,56 ----
***************
*** 61,64 ****
--- 60,64 ----
  void add_common_vars(request_rec *r);
  int scan_script_header(request_rec *r, FILE *f);
  void send_size(size_t size, request_rec *r);
+ int is_scriptaliased (request_rec *r);
  
*** http_exec.c.orig	Wed Jul 10 15:18:01 1996
--- http_exec.c	Wed Jul 10 22:04:42 1996
***************
*** 0 ****
--- 1,277 ----
+ 
+ /* ====================================================================
+  * Copyright (c) 1995 The Apache Group.  All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  *
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice, this list of conditions and the following disclaimer. 
+  *
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in
+  *    the documentation and/or other materials provided with the
+  *    distribution.
+  *
+  * 3. All advertising materials mentioning features or use of this
+  *    software must display the following acknowledgment:
+  *    "This product includes software developed by the Apache Group
+  *    for use in the Apache HTTP server project (http://www.apache.org/)."
+  *
+  * 4. The names "Apache Server" and "Apache Group" must not be used to
+  *    endorse or promote products derived from this software without
+  *    prior written permission.
+  *
+  * 5. Redistributions of any form whatsoever must retain the following
+  *    acknowledgment:
+  *    "This product includes software developed by the Apache Group
+  *    for use in the Apache HTTP server project (http://www.apache.org/)."
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
+  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+  * OF THE POSSIBILITY OF SUCH DAMAGE.
+  * ====================================================================
+  *
+  * This software consists of voluntary contributions made by many
+  * individuals on behalf of the Apache Group and was originally based
+  * on public domain software written at the National Center for
+  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+  * For more information on the Apache Group and the Apache HTTP server
+  * project, please see <http://www.apache.org/>.
+  *
+  */
+ 
+ 
+ /*
+  * http_exec.c: functions to handle exec requests
+  * 
+  */
+ 
+ #include "httpd.h"
+ #include "http_conf_globals.h"
+ #include "http_core.h"
+ #include "http_log.h"
+ #include "util_script.h"
+ 
+ #include <pwd.h>
+ #include <grp.h>
+ 
+ 
+ void call_exec (child_data *data, int shellcmd) 
+ {
+     char **env;
+     
+ #ifdef RLIMIT_CPU
+     struct rlimit cpulim = { 9, 10 };
+ #endif
+ 
+ #ifdef RLIMIT_DATA
+     struct rlimit datalim = { 2000000, 2500000 };
+ #endif
+ 
+ #ifdef RLIMIT_NPROC
+     struct rlimit proclim = { 20, 40 };
+ #endif
+     
+ #ifdef RLIMIT_VMEM
+     struct rlimit vmlim = { 2000000, 2500000 };
+ #endif
+     
+ #ifdef RLIMIT_CPU
+     setrlimit (RLIMIT_CPU, &cpulim);
+ #endif
+ 
+ #ifdef RLIMIT_DATA
+     setrlimit (RLIMIT_DATA, &datalim);
+ #endif
+ 
+ #ifdef RLIMIT_NPROC
+     setrlimit (RLIMIT_NPROC, &proclim);
+ #endif
+     
+ #ifdef RLIMIT_VMEM
+     setrlimit (RLIMIT_VMEM, &vmlim);
+ #endif
+     
+     can_exec(data);
+ 
+     if (data->retval)
+ 	return;
+     
+     add_cgi_vars (data->r);
+     env = create_environment (data->r->pool, data->r->subprocess_env);
+ 
+ #ifdef NOTYET
+     set_child_check_entry (data->magic_offset, NULL, NULL, data->r->server);
+ #endif
+ 
+ #ifdef __EMX__    
+     if ((!data->r->args) || (!data->r->args[0]) || (ind(data->r->args,'=') >= 0)) {
+ 	int emxloop;
+ 	char *emxtemp;
+ 
+ 	/* For OS/2 place the variables in the current
+ 	   enviornment then it will be inherited. This way
+ 	   the program will also get all of OS/2's other SETs. */
+ 	for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
+ 	    putenv(emxtemp);
+                 
+ 	if (strstr(strupr(data->r->filename), ".CMD") > 0) {
+ 	    /* Special case to allow use of REXX commands as scripts. */
+ 	    os2pathname(data->r->filename);
+ 	    execl("CMD.EXE", "CMD.EXE", "/C", data->r->filename, NULL);
+ 	}
+ 	else {
+ 	    execl(data->r->filename, data->cmdstr, NULL);
+ 	}
+     }
+     else {
+ 	int emxloop;
+ 	char *emxtemp;
+             
+ 	/* For OS/2 place the variables in the current
+ 	   enviornment then it will be inherited. This way
+ 	   the program will also get all of OS/2's other SETs. */
+ 	for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
+ 	    putenv(emxtemp);
+                 
+ 	if (strstr(strupr(data->r->filename), ".CMD") > 0) {
+ 	    /* Special case to allow use of REXX commands as scripts. */
+ 	    os2pathname(data->r->filename);
+ 	    execv("CMD.EXE", create_argv_cmd(data->r->pool, data->cmdstr,
+ 					     data->r->args, data->r->filename));
+ 	}
+ 	else
+ 	    execv(data->r->filename,
+ 		  create_argv(data->r->pool, data->cmdstr, data->r->args));
+     }
+ #else
+ 
+     if (shellcmd) 
+ 	execle(SHELL_PATH, SHELL_PATH, "-c", data->cmdstr, NULL, env);
+ 
+     else if((!data->r->args) || (!data->r->args[0]) || (ind(data->r->args,'=') >= 0))
+ 	execle(data->r->filename, data->cmdstr, NULL, env);
+ 
+     else
+ 	execve(data->r->filename,
+ 	       create_argv(data->r->pool, data->cmdstr, data->r->args), env);
+     
+ #endif
+ }
+ 
+ 
+ void can_exec (child_data *data)
+ {
+     int nph;
+     int is_included;
+     char *argv0;
+     char *lenp;
+     char *current_dir = '\0';
+     struct stat dir_info;
+ 
+ 
+     argv0 = data->r->filename;
+     lenp = table_get (data->r->headers_in, "Content-length");
+     nph = !(strncmp(argv0,"nph-",4));
+     is_included = !strcmp (data->r->protocol, "INCLUDED");
+     
+     if (!(allow_options (data->r) & OPT_EXECCGI) && !is_scriptaliased (data->r)) {
+         log_reason("Options ExecCGI is off in this directory", data->r->filename, data->r);
+ 	data->retval = FORBIDDEN;
+ 	return;
+     }
+     if (nph && is_included) {
+         log_reason("attempt to include NPH CGI script", data->r->filename, data->r);
+ 	data->retval = FORBIDDEN;
+ 	return;
+     }
+     if (S_ISDIR(data->r->finfo.st_mode)) {
+         log_reason("attempt to invoke directory as script", data->r->filename, data->r);
+ 	data->retval = FORBIDDEN;
+ 	return;
+     }
+     if (data->r->finfo.st_mode == 0) {
+         log_reason("script not found or unable to stat", data->r->filename, data->r);
+ 	data->retval = NOT_FOUND; 
+ 	return;
+     }
+     if ((data->r->method_number == M_POST || data->r->method_number == M_PUT) && !lenp) {
+         log_reason("POST or PUT without Content-length:",
+ 		   data->r->filename, data->r);
+ 	data->retval = BAD_REQUEST;
+ 	return;
+     }
+ 
+ #ifdef __EMX__
+     /* OS/2 dosen't have Users and Groups */
+     data->retval = (data->r->finfo.st_mode & S_IEXEC);
+     return;
+ #else
+     current_dir = make_dirstr (data->r->pool, data->r->filename,
+ 			       count_dirs(data->r->filename));
+ 
+     if ((stat (current_dir, &dir_info)) && !(S_ISDIR(dir_info.st_mode))) {
+ 	log_reason ("cannot stat directory", current_dir, data->r);
+ 	data->retval = NOT_FOUND;
+ 	return;
+     }
+     if (dir_info.st_mode & S_IWOTH || dir_info.st_mode & S_IWGRP) {
+ 	log_reason ("directory is writable by others - cannot execute",
+ 		    data->r->filename, data->r);
+ 	data->retval =  FORBIDDEN;
+ 	return;
+     }
+     if (data->r->finfo.st_mode & S_IWOTH || data->r->finfo.st_mode & S_IWGRP) {
+ 	log_reason ("file is writable by others - cannot execute",
+ 		    data->r->filename, data->r);
+ 	data->retval = FORBIDDEN;
+ 	return;
+     }
+ #ifdef NOTYET
+     if (data->r->server->server_uid != dir_info.st_uid ||
+ 	data->r->server->server_gid != dir_info.st_gid) {
+ 	log_reason("file owner/group and directory owner/group do not match server",
+ 		   data->r->filename, data->r);
+ 	data->retval = FORBIDDEN;
+ 	return;
+     }
+     if(data->r->server->server_uid == data->r->finfo.st_uid)
+         if(!(data->r->finfo.st_mode & S_IXUSR)) {
+ 	    log_reason("file permissions deny owner execution", data->r->filename, data->r);
+ 	    data->retval = FORBIDDEN;
+ 	    return;
+ 	}
+ 	else {
+ 	    data->retval = 0;
+ 	    return;
+ 	}
+     	    
+     if(data->r->server->server_gid == data->r->finfo.st_gid)
+         if(!(data->r->finfo.st_mode & S_IXGRP)) {
+ 	    log_reason("file permissions deny group execution", data->r->filename, data->r);
+ 	    data->retval = FORBIDDEN;
+             return;
+ 	}
+ 	else {
+ 	    data->retval = 0;
+ 	    return;
+ 	}
+ #endif
+     
+     data->retval = (data->r->finfo.st_mode & S_IXOTH);
+     return;
+ #endif    
+ }
+ 
+