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/06/02 18:12:46 UTC

setuid control WITHOUT running as root

Finally...

I was having a look at Jason Dour's mod_sucgi and realized that
coupled with the changes I have been running and his external
setuid wrapper, we were close to accomplishing what I have been
attempting for months. (duh)

I'm including below patches that allow you to define an execution
userid on a per/vhost basis. A vhost config that does not specify
"User" or "Group" will simply revert to the main userid/groupid
of the server.

The patch centralizes our exec() stuff into http_exec.c and uses
call_exec() in the various modules that need to do this. can_exec()
is greatly enhanced to do serveral other checks before passing on.

After everything passes, the arguments are sent to the sucgi.c
wrapper that then does a few other sanity checks before the
execution.

By centralizing the exec code, it makes it much easier to add
the same functionality to mod_include, etc.

There is more than likely some excess baggage from my previous
hacks. I would mainly like to get this in front of the group
so that some more eyes could spot potential problems. Comments
on level of thread safety are solicited. 

I fully realize that this can not be considered for 1.1, but I
would love to see it go into the 1.2 base so I can get on to
other maintenance issues.

Known weaknesses:

sucgi.c probably needs to completely dissallow requests for uid 0
execution, or at least have a list of acceptable scripts that
can be run as such.

It would also be nice to have a list of allowable scripts for certain
hosts.

Use 'patch -E' since this patch creates 2 non-existant files.


Index: src/Makefile.tmpl
===================================================================
RCS file: /export/home/cvs/apache/src/Makefile.tmpl,v
retrieving revision 1.12
diff -c -r1.12 Makefile.tmpl
*** Makefile.tmpl	1996/05/29 11:46:40	1.12
--- Makefile.tmpl	1996/06/02 15:48:30
***************
*** 3,11 ****
  # 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)
  
  .c.o:
  	$(CC) -c $(CFLAGS) $(AUX_CFLAGS) $<
--- 3,11 ----
  # 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)
  
  .c.o:
  	$(CC) -c $(CFLAGS) $(AUX_CFLAGS) $<
Index: src/http_core.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_core.c,v
retrieving revision 1.12
diff -c -r1.12 http_core.c
*** http_core.c	1996/05/22 17:35:36	1.12
--- http_core.c	1996/06/02 15:48:45
***************
*** 359,365 ****
      void *sconf = cmd->server->module_config;
      core_server_config *conf = get_module_config (sconf, &core_module);
    
!     if (!is_directory (arg)) return "DocumentRoot must be a directory";
      
      conf->document_root = arg;
      return NULL;
--- 359,369 ----
      void *sconf = cmd->server->module_config;
      core_server_config *conf = get_module_config (sconf, &core_module);
    
!     if (!is_directory (arg))
! 	if (cmd->server->is_virtual)
! 	    fprintf (stderr, "Warning: DocumentRoot [%s] does not exist\n", arg);
! 	else
! 	    return "DocumentRoot must be a directory";
      
      conf->document_root = arg;
      return NULL;
***************
*** 575,580 ****
--- 579,591 ----
  char *end_virthost_magic = "</Virtualhost> out of place";
  
  char *end_virtualhost_section (cmd_parms *cmd, void *dummy) {
+ 
+     if (cmd->server->server_uid == NULL)
+ 	cmd->server->server_uid = uname2id (user_name);
+ 
+     if (cmd->server->server_gid == NULL)
+ 	cmd->server->server_gid = group_id;
+     
      return end_virthost_magic;
  }
  
***************
*** 607,612 ****
--- 618,662 ----
      return errmsg;
  }
  
+ char *read_configdir (cmd_parms *cmd, void *dummy, char *arg)
+ {
+     DIR *vdir;
+     struct DIR_TYPE *vdir_entry;
+     char *dirname, *fname, *p;
+     
+     dirname = server_root_relative (cmd->pool, arg);
+     
+     if (!is_directory (dirname))
+ 	log_error ("ConfigDir must be a valid directory", cmd->server);
+     
+     vdir = opendir (dirname);
+ 
+     if (vdir == NULL) {
+         return NULL;
+     }
+ 
+     if ((p = strrchr (dirname, '\0')))
+     {
+ 	if (*--p != '/')
+ 	    dirname = pstrcat (cmd->pool, dirname, "/", NULL);
+     }
+     
+     while ((vdir_entry = readdir (vdir))) {
+ 
+ 	if ((strstr(vdir_entry->d_name, ".conf")) != NULL)
+ 	{
+ 	    fname = pstrcat (cmd->pool, dirname, vdir_entry->d_name, NULL);
+ 	    process_resource_config (cmd->server, fname,
+ 				     cmd->pool, cmd->temp_pool);
+ 	}
+ 	else
+ 	    continue;
+     }
+ 
+     closedir (vdir);
+     return NULL;
+ }
+ 
  char *set_server_string_slot (cmd_parms *cmd, void *dummy, char *arg)
  {
      /* This one's pretty generic... */
***************
*** 633,645 ****
  }
  
  char *set_user (cmd_parms *cmd, void *dummy, char *arg) {
!     user_name = pstrdup (cmd->pool, arg);
!     user_id = uname2id (user_name);
      return NULL;
  }
  
  char *set_group (cmd_parms *cmd, void *dummy, char *arg) {
!     group_id = gname2id(arg);
      return NULL;
  }
  
--- 683,710 ----
  }
  
  char *set_user (cmd_parms *cmd, void *dummy, char *arg) {
!     if (!cmd->server->is_virtual)
!     {
! 	user_name = pstrdup (cmd->pool, arg);
! 	user_id = uname2id (user_name);
!     }
!     
!     cmd->server->server_uid = uname2id (arg);
! 
      return NULL;
  }
  
  char *set_group (cmd_parms *cmd, void *dummy, char *arg) {
! 
!     gid_t garg;
!     
!     garg = gname2id(arg);
! 
!     if (!cmd->server->is_virtual)
! 	group_id = garg;
!     
!     cmd->server->server_gid = garg;
! 
      return NULL;
  }
  
***************
*** 790,797 ****
  { "ServerType", server_type, NULL, RSRC_CONF, TAKE1,"'inetd' or 'standalone'"},
  { "Port", server_port, NULL, RSRC_CONF, TAKE1, "a TCP port number"},
  { "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, FLAG, NULL },
! { "User", set_user, NULL, RSRC_CONF, TAKE1, "a username"},
! { "Group", set_group, NULL, RSRC_CONF, TAKE1, "a group name"},
  { "ServerAdmin", set_server_string_slot,
    (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1,
    "The email address of the server administrator" },
--- 855,862 ----
  { "ServerType", server_type, NULL, RSRC_CONF, TAKE1,"'inetd' or 'standalone'"},
  { "Port", server_port, NULL, RSRC_CONF, TAKE1, "a TCP port number"},
  { "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, FLAG, NULL },
! { "User", set_user, NULL, RSRC_CONF, TAKE1, "effective user id for this server"},
! { "Group", set_group, NULL, RSRC_CONF, TAKE1, "effective group id for this server"},
  { "ServerAdmin", set_server_string_slot,
    (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1,
    "The email address of the server administrator" },
***************
*** 834,839 ****
--- 899,905 ----
        "a port number or a numeric IP address and a port number"},
  { "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS, NULL },
  { "</VirtualHost>", end_virtualhost_section, NULL, RSRC_CONF, NO_ARGS, NULL },
+ { "ConfigDir", read_configdir, NULL, RSRC_CONF, TAKE1, "directory containing config files" },
  { NULL },
  };
  
Index: src/httpd.h
===================================================================
RCS file: /export/home/cvs/apache/src/httpd.h,v
retrieving revision 1.21
diff -c -r1.21 httpd.h
*** httpd.h	1996/05/27 19:48:38	1.21
--- httpd.h	1996/06/02 15:49:12
***************
*** 427,469 ****
  
  struct server_rec {
  
!   server_rec *next;
    
!   /* Full locations of server config info */
    
!   char *srm_confname;
!   char *access_confname;
    
!   /* Contact information */
    
!   char *server_admin;
!   char *server_hostname;
!   short port;                    /* for redirects, etc. */
    
!   /* Log files --- note that transfer log is now in the modules... */
    
!   char *error_fname;
!   FILE *error_log;
    
!   /* Module-specific configuration for server, and defaults... */
  
!   int is_virtual;               /* true if this is the virtual server */
!   void *module_config;		/* Config vector containing pointers to
  				 * modules' per-server config structures.
  				 */
!   void *lookup_defaults;	/* MIME type info, etc., before we start
  				 * checking per-directory info.
  				 */
!   /* Transaction handling */
  
!   struct in_addr host_addr;	/* The bound address, for this server */
!   short host_port;              /* The bound port, for this server */
!   int timeout;			/* Timeout, in seconds, before we give up */
!   int keep_alive_timeout;	/* Seconds we'll wait for another request */
!   int keep_alive;		/* Maximum requests per connection */
  
-   char *names;			/* Wildcarded names for HostAlias servers */
-   char *virthost;		/* The name given in <VirtualHost> */
  };
  
  /* These are more like real hosts than virtual hosts */
--- 427,474 ----
  
  struct server_rec {
  
!     server_rec *next;
    
!     /* Full locations of server config info */
    
!     char *srm_confname;
!     char *access_confname;
    
!     /* Contact information */
    
!     char *server_admin;
!     char *server_hostname;
!     short port;                    /* for redirects, etc. */
    
!     /* Log files --- note that transfer log is now in the modules... */
    
!     char *error_fname;
!     FILE *error_log;
    
!     /* Module-specific configuration for server, and defaults... */
  
!     int is_virtual;             /* true if this is the virtual server */
!     void *module_config;	/* Config vector containing pointers to
  				 * modules' per-server config structures.
  				 */
!     void *lookup_defaults;	/* MIME type info, etc., before we start
  				 * checking per-directory info.
  				 */
!     /* Transaction handling */
  
!     struct in_addr host_addr;	/* The bound address, for this server */
!     short host_port;            /* The bound port, for this server */
!     int timeout;		/* Timeout, in seconds, before we give up */
!     int keep_alive_timeout;	/* Seconds we'll wait for another request */
!     int keep_alive;		/* Maximum requests per connection */
! 
!     char *names;		/* Wildcarded names for HostAlias servers */
!     char *virthost;		/* The name given in <VirtualHost> */
! 
!     /* effective user/group id when calling exec wrapper */
!     pid_t server_uid;
!     gid_t server_gid;
  
  };
  
  /* These are more like real hosts than virtual hosts */
***************
*** 523,529 ****
  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 *);
--- 528,535 ----
  uid_t uname2id(char *name);
  gid_t gname2id(char *name);
  int is_directory(char *name);
! int can_exec(request_rec *);     
! void call_exec(request_rec *r, char *argv0, 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.8
diff -c -r1.8 mod_cgi.c
*** mod_cgi.c	1996/05/22 17:35:38	1.8
--- mod_cgi.c	1996/06/02 15:49:26
***************
*** 74,90 ****
  #include "http_log.h"
  #include "util_script.h"
  
- /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
-  * in ScriptAliased directories, which means we need to know if this
-  * request came through ScriptAlias or not... so the Alias module
-  * leaves a note for us.
-  */
- 
- int is_scriptaliased (request_rec *r)
- {
-     char *t = table_get (r->notes, "alias-forced-type");
-     return t && (!strcmp (t, "cgi-script"));
- }
  
  /****************************************************************
   *
--- 74,79 ----
***************
*** 102,108 ****
  {
      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    
--- 91,96 ----
***************
*** 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]);
--- 103,116 ----
      int i;
  #endif
      
      char err_string[HUGE_STRING_LEN];
      
  #ifdef DEBUG_CGI    
+     char *argv0 = cld->argv0;
      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!
--- 129,135 ----
      
      cleanup_for_exec();
      
!     call_exec(r, cld->argv0, 0);
  
      /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
       * EARTH-shattering kaboom!
***************
*** 207,216 ****
  int cgi_handler (request_rec *r)
  {
      int nph;
      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;
--- 151,160 ----
  int cgi_handler (request_rec *r)
  {
      int nph;
+     int exec_check;
      char *argv0;
      FILE *script_out, *script_in;
      char argsbuffer[HUGE_STRING_LEN];
      char *lenp = table_get (r->headers_in, "Content-length");
  
      struct cgi_child_stuff cld;
***************
*** 221,255 ****
  
      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) {
--- 165,176 ----
  
      nph = !(strncmp(argv0,"nph-",4));
      
!     cld.argv0 = argv0; cld.r = r; cld.nph = nph;
      
      add_common_vars (r);
! 
!     if ((exec_check = can_exec(r)))
! 	return exec_check;
      
  #ifdef __EMX__
      if (r->method_number == M_POST || r->method_number == M_PUT) {
Index: src/util.c
===================================================================
RCS file: /export/home/cvs/apache/src/util.c,v
retrieving revision 1.10
diff -c -r1.10 util.c
*** util.c	1996/05/27 19:48:40	1.10
--- util.c	1996/06/02 15:49:57
***************
*** 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 ----
*** http_exec.c.orig	Sat Jun  1 23:40:35 1996
--- http_exec.c	Sun Jun  2 10:37:37 1996
***************
*** 0 ****
--- 1,237 ----
+ 
+ /* ====================================================================
+  * 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_core.h"
+ #include "http_log.h"
+ #include "util_script.h"
+ 
+ #include <pwd.h>
+ #include <grp.h>
+ 
+ #define SUCGI_BIN "/www/bin/sucgi"
+ 
+ /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
+  * in ScriptAliased directories, which means we need to know if this
+  * request came through ScriptAlias or not... so the Alias module
+  * leaves a note for us.
+  */
+ 
+ int is_scriptaliased (request_rec *r)
+ {
+     char *t = table_get (r->notes, "alias-forced-type");
+     return t && (!strcmp (t, "cgi-script"));
+ }
+ 
+ 
+ void
+ call_exec (request_rec *r, char *argv0, int shellcmd) 
+ {
+     char **env;
+     struct passwd *pw;
+     struct group *gr;
+     
+     add_cgi_vars (r);
+     env = create_environment (r->pool, r->subprocess_env);
+ 
+     pw = (struct passwd *) palloc (r->pool, sizeof (struct passwd));
+     gr = (struct group *) palloc (r->pool, sizeof (struct group));
+     
+ #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
+ 
+     pw = getpwuid (r->server->server_uid);
+     gr = getgrgid (r->server->server_gid);
+     
+     if (shellcmd)
+     	execle(SUCGI_BIN, SUCGI_BIN, pw->pw_name, gr->gr_name, argv0, NULL, env);
+ 
+     else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
+ 	execle(SUCGI_BIN, SUCGI_BIN, pw->pw_name, gr->gr_name, argv0, NULL, env);
+ 
+     else
+ 	execle(SUCGI_BIN, SUCGI_BIN, pw->pw_name, gr->gr_name,
+ 	       create_argv(r->pool, argv0, r->args), NULL, env);
+     
+ #endif
+ }
+ 
+ 
+ int
+ can_exec(request_rec *r) {
+ 
+     int nph;
+     int is_included;
+     char *argv0;
+     char *lenp;
+     char *current_dir = '\0';
+     
+     struct stat dir_info;
+ 
+     argv0 = r->filename;
+     lenp = table_get (r->headers_in, "Content-length");
+     nph = !(strncmp(argv0,"nph-",4));
+     is_included = !strcmp (r->protocol, "INCLUDED");
+     
+     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 ((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;
+     }
+ 
+ #ifdef __EMX__
+     /* OS/2 dosen't have Users and Groups */
+     return (r->finfo.st_mode & S_IEXEC);
+ #else
+     current_dir = make_dirstr (r->pool, r->filename, count_dirs(r->filename));
+ 
+     if ((stat (current_dir, &dir_info)) && !(S_ISDIR(dir_info.st_mode))) {
+ 	log_reason ("cannot stat directory", current_dir, r);
+ 	return NOT_FOUND;
+     }
+     if (dir_info.st_mode & S_IWOTH || dir_info.st_mode & S_IWGRP) {
+ 	log_reason ("directory is writable by others - cannot execute", r->filename, r);
+ 	return FORBIDDEN;
+     }
+     if (r->finfo.st_mode & S_IWOTH || r->finfo.st_mode & S_IWGRP) {
+ 	log_reason ("file is writable by others - cannot execute", r->filename, r);
+ 	return FORBIDDEN;
+     }
+     if (r->server->server_uid != dir_info.st_uid &&
+ 	r->server->server_gid != dir_info.st_gid) {
+ 	log_reason("file owner/group and directory owner/group do not match server",
+ 		   r->filename, r);
+ 	return FORBIDDEN;
+     }
+     if(r->server->server_uid == r->finfo.st_uid)
+         if(!(r->finfo.st_mode & S_IXUSR)) {
+ 	    log_reason("file permissions deny owner execution", r->filename, r);
+ 	    return FORBIDDEN;
+ 	}
+ 	else return 0;
+     	    
+     if(r->server->server_gid == r->finfo.st_gid)
+         if(!(r->finfo.st_mode & S_IXGRP)) {
+ 	    log_reason("file permissions deny group execution", r->filename, r);
+             return FORBIDDEN;
+ 	}
+ 	else return 0;
+     
+     return (r->finfo.st_mode & S_IXOTH);
+ #endif    
+ }
+ 
+ 
*** sucgi.c.orig	Sat Jun  1 23:40:44 1996
--- sucgi.c	Sun Jun  2 10:44:57 1996
***************
*** 0 ****
--- 1,89 ----
+ /*
+ #
+ # sucgi.c -- "wrapper" support program for mod_sucgi for Apache
+ #
+ # A MotherSoft Product for the Apache WWW server.
+ # (http://www.louisville.edu/~jadour01/mothersoft/)
+ #
+ # Codebase originally from Majordomo(v1.93) release.  Modifications
+ # by Jason A. Dour (jadour01@homer.louisville.edu).
+ #
+ # NOTE! : Due to the sensitive nature of this program, its error messages
+ #         are left intentionally unhelpful...
+ #
+ # WHAT_USER - Set this to the username that is allowed to execute
+ #             this program.  Most likely, it's some generic user
+ #             such as 'httpd' or some such...
+ #
+ # Free for distribution, copying, editing, and hacking under the Apache
+ # license.  See the Apache license for specific information.  This software
+ # comes with no guarantees implicit or implied, and the author(s) of this
+ # software cannot be held responsible for loss, damage, acts of god(s),
+ # large amounts of small rodentia, deafness, plague, baldness, or
+ # nose-bleeds occurring as a direct -- or indirect -- result of the use
+ # of this MotherSoft product.  This software is to be used for MOTHERing,
+ # weirdness, taking care of animals, peace, love, and spreading genuine 
+ # feelings of well being.  All other uses are denounced by the author(s).
+ #
+ # Love, Peace, Gerbils, & Hair Grease,
+ # Jason A. Dour
+ #
+ */
+ 
+ #define WHAT_USER "www"
+ 
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include <strings.h>
+ #include <pwd.h>
+ #include <grp.h>
+ 
+ 
+ int
+ main(int argc, char *argv[], char **env)
+ {
+     uid_t uid;
+     struct passwd *pw;
+     struct group *gr;
+ 
+     uid = getuid();
+     if ((pw = getpwuid (uid)) == NULL)
+     {
+ 	fprintf (stderr, "(%s): error code 1 (%ld)\n", argv[0], uid);
+ 	exit (101);
+     }
+     
+     if (strcmp (WHAT_USER, pw->pw_name)) {
+         fprintf(stderr, "(%s): error code 2 (%s)\n", argv[0], pw->pw_name);
+         exit(102);
+     }
+     
+     if (argc < 4) {
+ 	fprintf(stderr, "(%s): error code 3\n", argv[0]);
+ 	exit(103);
+     }
+ 
+     if ( strchr(argv[3], '/') != (char) NULL ) {
+         fprintf(stderr, "(%s): error code 4 (%s)\n", argv[0], argv[3]);
+         exit(104);
+     }
+ 
+     pw = getpwnam (argv[1]);
+     if (setuid(pw->pw_uid) != 0) {
+         fprintf(stderr, "(%s): error code 5 (%d)\n", argv[0], pw->pw_uid);
+         exit(105);
+     }
+ 
+     gr = getgrnam (argv[2]);
+     if (setgid(gr->gr_gid) != 0) {
+         fprintf(stderr, "(%s): error code 6 (%d)\n", argv[0], gr->gr_gid);
+         exit(106);
+     }
+ 
+     execve(argv[3], &argv[4], env);
+ 
+     perror(argv[3]);
+     exit(255);
+ }