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