The following reply was made to PR suexec/1769; it has been noted by GNATS.
From: Gary Shea <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
Cc: [EMAIL PROTECTED], [EMAIL PROTECTED], [EMAIL PROTECTED]
Subject: Re: suexec/1769: suexec too limited -- need per-directory control,
more permissive directory structures
Date: Fri, 6 Feb 1998 13:30:23 -0700 (MST)
As promised, patches. These patches were inspired by, and to some
extent stolen from, Philippe Vanhaesendonck's ([EMAIL PROTECTED])
mod_cgi_sugid patches. Philippe's code/concept functioned entirely
in mod_cgi and is no longer workable in 1.3b3 (I've not looked at
1.2.* since 1.2b10), but is the source of the UserID and GroupID
directive support code.
I have hacked mod_cgi to directly request a uid/gid from call_exec()
if there is an applicable UserID and/or GroupID. call_exec() respects
directly requested uid/gid's absolutely, but if none is specified, it'll
still detect ~user uri's and pass the appropriate uid/gid to suexec.
Thus it's identical from the point of view of mod_include, but can
still give mod_cgi what it wants/expects..
suexec is heavily hacked to suck up a configuration file more or less
akin to standard apache config files, but I didn't attempt to use
apache code -- it's my proof of concept I guess, easier to code it
for testing than to work out integrating the apache code. As a result
the config files are quite picky about case, etc.. Also there
are some holes still insofar as what happens if some of the
directives are not specified. I won't bother to fix those holes
unless someone else wants to use this code.
The configuration file takes three directives at present;
everything else remains compiled in. Here's an example.
UserDirSuffix: public_html/cgi-bin
UserDirectories: on
Directory: /users/src/a13b3/htd3/cgi-bin
Directory: /users/src/a13b3/htd2/cgi-bin
Directory: /users/src/a13b3/htd5/cgi-bin
I have not yet converted suexec to a simple forking server sitting
on a Unix socket, but if ther are adverse effects from startup time,
I will. Since most of what I run are large Perl codes that take close
to a second to compile, that's unlikely!
Here's a clip of a configuration file which uses the UserID and GroupID
directives:
ScriptAlias /htd2/cgi-bin /users/src/a13b3/htd2/cgi-bin
<Location /htd2/cgi-bin>
<Limit GET POST>
UserId shea
GroupId users
</Limit>
</Location>
And.... HEEEEEEEEEEERE'S the PATCHES!
diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/Configuration
a13b3/src/Configuration
*** apache_1.3b3/src/Configuration Wed Nov 19 17:49:57 1997
--- a13b3/src/Configuration Wed Feb 4 22:48:13 1998
***************
*** 41,47 ****
# Settings here have priority; If not set, Configure will attempt to guess
# the C compiler, looking for gcc first, then cc.
#
! EXTRA_CFLAGS=
EXTRA_LDFLAGS=
EXTRA_LIBS=
EXTRA_INCLUDES=
--- 41,47 ----
# Settings here have priority; If not set, Configure will attempt to guess
# the C compiler, looking for gcc first, then cc.
#
! EXTRA_CFLAGS=-DHTTPD_ROOT=\"/users/src/a13b3\" -DDEBUG_CGI
-DDEBUG_SUGID_CONFIG -DSUEXEC_BIN=\"/users/src/a13b3/sbin/suexec\"
EXTRA_LDFLAGS=
EXTRA_LIBS=
EXTRA_INCLUDES=
***************
*** 181,192 ****
## STATUS=yes (see the Rules section near the start of this file) to allow
## full status information. Check conf/access.conf on how to enable this.
! # AddModule modules/standard/mod_status.o
## The Info module displays configuration information for the server and
## all included modules. It's very useful for debugging.
! # AddModule modules/standard/mod_info.o
## mod_include translates server-side include (SSI) statements in text files.
## mod_autoindex handles requests for directories which have no index file
--- 181,192 ----
## STATUS=yes (see the Rules section near the start of this file) to allow
## full status information. Check conf/access.conf on how to enable this.
! AddModule modules/standard/mod_status.o
## The Info module displays configuration information for the server and
## all included modules. It's very useful for debugging.
! AddModule modules/standard/mod_info.o
## mod_include translates server-side include (SSI) statements in text files.
## mod_autoindex handles requests for directories which have no index file
diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/main/util_script.c
a13b3/src/main/util_script.c
*** apache_1.3b3/src/main/util_script.c Sun Nov 16 08:45:22 1997
--- a13b3/src/main/util_script.c Fri Feb 6 08:47:03 1998
***************
*** 559,567 ****
#endif
! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int
shellcmd)
{
int pid = 0;
#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
--- 559,569 ----
#endif
! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int
shellcmd, uid_t req_uid, gid_t req_gid)
{
int pid = 0;
+ int change_ids = 0; /* Fork suexec or (by default) the target command? */
+ char *execuser, *grpname; /* Only used if change_ids gets set. Ick. */
#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
defined(RLIMIT_DATA) || defined(RLIMIT_VMEM)
***************
*** 778,840 ****
return (pid);
}
#else
! if (suexec_enabled &&
! ((r->server->server_uid != user_id) ||
! (r->server->server_gid != group_id) ||
! (!strncmp("/~", r->uri, 2)))) {
!
! char *execuser, *grpname;
! struct passwd *pw;
! struct group *gr;
!
! if (!strncmp("/~", r->uri, 2)) {
! gid_t user_gid;
! char *username = pstrdup(r->pool, r->uri + 2);
! int pos = ind(username, '/');
! if (pos >= 0)
! username[pos] = '\0';
! if ((pw = getpwnam(username)) == NULL) {
aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getpwnam: invalid username %s", username);
! return (pid);
! }
! execuser = pstrcat(r->pool, "~", pw->pw_name, NULL);
! user_gid = pw->pw_gid;
!
! if ((gr = getgrgid(user_gid)) == NULL) {
! if ((grpname = palloc(r->pool, 16)) == NULL)
! return (pid);
! else
! ap_snprintf(grpname, 16, "%ld", (long) user_gid);
! }
! else
! grpname = gr->gr_name;
! }
! else {
! if ((pw = getpwuid(r->server->server_uid)) == NULL) {
! aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getpwuid: invalid userid %ld",
! (long) r->server->server_uid);
return (pid);
}
execuser = pstrdup(r->pool, pw->pw_name);
! if ((gr = getgrgid(r->server->server_gid)) == NULL) {
aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getgrgid: invalid groupid %ld",
! (long) r->server->server_gid);
return (pid);
}
! grpname = gr->gr_name;
}
if (shellcmd)
! execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
else if ((!r->args) || (!r->args[0]) || (ind(r->args, '=') >= 0))
! execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
else {
execve(SUEXEC_BIN,
--- 780,849 ----
return (pid);
}
#else
! if (suexec_enabled) {
! uid_t target_uid;
! gid_t target_gid;
!
! if (req_uid > 0 || req_gid > 0) {
! /*
! * In the cgi module, the user id and group id are requested
! * independently, so provide sensible defaults in case one
! * is not specified.
! */
! target_uid = req_uid > 0 ? req_uid : r->server->server_uid;
! target_gid = req_gid > 0 ? req_gid : r->server->server_gid;
! } else if (strncmp("/~", r->uri, 2) == 0) {
! struct passwd *pw;
! char *username = pstrdup(r->pool, r->uri + 2);
! int pos = ind(username, '/');
!
! if (pos >= 0)
! username[pos] = '\0';
!
! if ((pw = getpwnam(username)) == NULL) {
! aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getpwnam: invalid username %s", username);
! exit (0);
! }
! target_uid = pw->pw_uid;
! target_gid = pw->pw_gid;
! } else {
! target_uid = r->server->server_uid;
! target_gid = r->server->server_gid;
! }
!
! if (target_uid != user_id || target_gid != group_id) {
! struct passwd *pw;
! struct group *gr;
! change_ids = 1;
! if ((pw = getpwuid(target_uid)) == NULL) {
aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getpwuid: invalid userid %ld",
! (long) target_uid);
return (pid);
}
execuser = pstrdup(r->pool, pw->pw_name);
! if ((gr = getgrgid(target_gid)) == NULL) {
aplog_error(APLOG_MARK, APLOG_ERR, r->server,
! "getgrgid: invalid groupid %ld",
! (long) target_gid);
return (pid);
}
! grpname = pstrdup(r->pool, gr->gr_name);
}
+ }
+ if (change_ids) {
if (shellcmd)
! execle(SUEXEC_BIN,
! SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
else if ((!r->args) || (!r->args[0]) || (ind(r->args, '=') >= 0))
! execle(SUEXEC_BIN,
! SUEXEC_BIN, execuser, grpname, argv0, NULL, env);
else {
execve(SUEXEC_BIN,
***************
*** 842,849 ****
argv0, r->args),
env);
}
! }
! else {
if (shellcmd)
execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
--- 851,857 ----
argv0, r->args),
env);
}
! } else {
if (shellcmd)
execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/main/util_script.h
a13b3/src/main/util_script.h
*** apache_1.3b3/src/main/util_script.h Wed Oct 22 14:29:53 1997
--- a13b3/src/main/util_script.h Fri Feb 6 08:48:53 1998
***************
*** 67,70 ****
API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char
*buffer);
API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char
*buffer);
API_EXPORT(void) send_size(size_t size, request_rec *r);
! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int
shellcmd);
--- 67,70 ----
API_EXPORT(int) scan_script_header_err(request_rec *r, FILE *f, char
*buffer);
API_EXPORT(int) scan_script_header_err_buff(request_rec *r, BUFF *f, char
*buffer);
API_EXPORT(void) send_size(size_t size, request_rec *r);
! API_EXPORT(int) call_exec(request_rec *r, char *argv0, char **env, int
shellcmd, uid_t request_uid, gid_t request_gid);
Only in a13b3/src/modules: CVS
Only in a13b3/src/modules: Makefile
Only in a13b3/src/modules/standard: CVS
Only in a13b3/src/modules/standard: Makefile
diff -c --exclude=*.[oa] --recursive
apache_1.3b3/src/modules/standard/mod_cgi.c a13b3/src/modules/standard/mod_cgi.c
*** apache_1.3b3/src/modules/standard/mod_cgi.c Fri Nov 7 18:20:02 1997
--- a13b3/src/modules/standard/mod_cgi.c Fri Feb 6 08:47:43 1998
***************
*** 99,104 ****
--- 99,111 ----
int bufbytes;
} cgi_server_conf;
+ typedef struct
+ {
+ uid_t userid;
+ gid_t groupid;
+ } cgi_dir_conf;
+
+
static void *create_cgi_config(pool *p, server_rec *s)
{
cgi_server_conf *c =
***************
*** 118,123 ****
--- 125,144 ----
return overrides->logname ? overrides : base;
}
+ void *create_cgi_dir_config (pool *p, char *dummy)
+ {
+ cgi_dir_conf *c = (cgi_dir_conf *) palloc (p,sizeof(cgi_dir_conf));
+
+ # ifdef DEBUG_SUGID_CONFIG
+ fprintf(stderr,"Create dir config: %s\n",dummy ? dummy : "<NULL>");
+ # endif
+
+ c->userid = 0;
+ c->groupid = 0;
+
+ return (void *) c;
+ }
+
static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
{
server_rec *s = cmd->server;
***************
*** 148,153 ****
--- 169,219 ----
return NULL;
}
+ const char *set_userid(cmd_parms *cmd,cgi_dir_conf *conf, char *user) {
+ struct passwd *ent;
+
+ # ifdef DEBUG_SUGID_CONFIG
+ fprintf(stderr,"Add UserId: %s\n",user);
+ fprintf(stderr,"\tPrevious value: %d\n",conf->userid);
+ fprintf(stderr,"\tConfig File : %s\n",cmd->config_file->name);
+ fprintf(stderr,"\tPath : %s\n",
+ cmd->path ? cmd->path : "<NULL>");
+ # endif
+
+ if (user[0] == '#') {
+ conf->userid = atoi(&user[1]);
+ return NULL;
+ } else if (!(ent = getpwnam(user))) {
+ return "Invalid User Name";
+ } else {
+ conf->userid = ent->pw_uid;
+ return NULL;
+ }
+ }
+
+ const char *set_groupid(cmd_parms *cmd,cgi_dir_conf *conf, char *grp)
+ {
+ struct group *ent;
+
+ # ifdef DEBUG_SUGID_CONFIG
+ fprintf(stderr,"Add GroupId: %s\n",grp);
+ fprintf(stderr,"\tPrevious value: %d\n",conf->groupid);
+ fprintf(stderr,"\tConfig File : %s\n",cmd->config_file->name);
+ fprintf(stderr,"\tPath : %s\n",
+ cmd->path ? cmd->path : "<NULL>");
+ # endif
+
+ if (grp[0] == '#') {
+ conf->groupid = atoi(&grp[1]);
+ return NULL;
+ } else if (!(ent = getgrnam(grp))) {
+ return "Invalid Group Name";
+ } else {
+ conf->groupid = ent->gr_gid;
+ return NULL;
+ }
+ }
+
static command_rec cgi_cmds[] =
{
{"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
***************
*** 156,161 ****
--- 222,231 ----
"the maximum length (in bytes) of the script debug log"},
{"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
"the maximum size (in bytes) to record of a POST request"},
+ { "UserId", set_userid, NULL, RSRC_CONF | ACCESS_CONF, TAKE1,
+ "a UserName or #UserId"},
+ { "GroupId", set_groupid, NULL, RSRC_CONF | ACCESS_CONF, TAKE1,
+ "a GroupName or #GroupId"},
{NULL}
};
***************
*** 281,286 ****
--- 351,361 ----
request_rec *r = cld->r;
char *argv0 = cld->argv0;
int child_pid;
+ cgi_dir_conf *conf = (cgi_dir_conf *)
+ get_module_config (r->per_dir_config,&cgi_module);
+ uid_t request_uid = 0;
+ gid_t request_gid = 0;
+
#ifdef DEBUG_CGI
#ifdef __EMX__
***************
*** 296,303 ****
RAISE_SIGSTOP(CGI_CHILD);
#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);
--- 371,378 ----
RAISE_SIGSTOP(CGI_CHILD);
#ifdef DEBUG_CGI
! fprintf(dbg, "%s (uri=<%s>) as CGI child (argv0 = %s)\n",
! r->filename, r->uri, argv0);
#endif
add_cgi_vars(r);
***************
*** 309,314 ****
--- 384,417 ----
fprintf(dbg, "'%s'\n", env[i]);
#endif
+
+ #if ! defined(__EMX__) && ! defined(WIN32)
+ /*
+ * See under which uid we will run the request.
+ * If there's a UserId or GroupId available, use those.
+ * The request_[ug]id args to call_exec are set non-zero
+ * to override the per-virtual or overall-server defaults.
+ * If they're left 0, 0, they will be set (in main/util_script.c)
+ * to a user's uid/gid if this is a ~user uri and suexec
+ * is enabled.
+ */
+ if (suexec_enabled) {
+ if (conf) {
+ if (conf->userid > 0) {
+ request_uid = conf->userid;
+ }
+ if (conf->groupid > 0) {
+ request_gid = conf->groupid;
+ }
+ }
+ #ifdef DEBUG_SUGID_CONFIG
+ fprintf(dbg, "sugid parameters: request_uid=%d request_gid=%d\n",
+ request_uid, request_gid);
+ #endif
+
+ }
+ #endif
+
chdir_file(r->filename);
if (!cld->debug)
error_log2stderr(r->server);
***************
*** 319,325 ****
cleanup_for_exec();
! child_pid = call_exec(r, argv0, env, 0);
#ifdef WIN32
return (child_pid);
#else
--- 422,428 ----
cleanup_for_exec();
! child_pid = call_exec(r, argv0, env, 0, request_uid, request_gid);
#ifdef WIN32
return (child_pid);
#else
***************
*** 551,557 ****
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
! NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_cgi_config, /* server config */
merge_cgi_config, /* merge server config */
--- 654,660 ----
{
STANDARD_MODULE_STUFF,
NULL, /* initializer */
! create_cgi_dir_config, /* dir config creater */
NULL, /* dir merger --- default is to override */
create_cgi_config, /* server config */
merge_cgi_config, /* merge server config */
diff -c --exclude=*.[oa] --recursive
apache_1.3b3/src/modules/standard/mod_include.c
a13b3/src/modules/standard/mod_include.c
*** apache_1.3b3/src/modules/standard/mod_include.c Sun Nov 9 13:40:34 1997
--- a13b3/src/modules/standard/mod_include.c Fri Feb 6 08:45:57 1998
***************
*** 733,739 ****
#endif
cleanup_for_exec();
/* set shellcmd flag to pass arg to SHELL_PATH */
! child_pid = call_exec(r, s, create_environment(r->pool, env), 1);
#ifdef WIN32
return (child_pid);
#else
--- 733,739 ----
#endif
cleanup_for_exec();
/* set shellcmd flag to pass arg to SHELL_PATH */
! child_pid = call_exec(r, s, create_environment(r->pool, env), 1, 0, 0);
#ifdef WIN32
return (child_pid);
#else
diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/support/suexec.c
a13b3/src/support/suexec.c
*** apache_1.3b3/src/support/suexec.c Wed Oct 22 14:30:46 1997
--- a13b3/src/support/suexec.c Fri Feb 6 07:43:58 1998
***************
*** 112,117 ****
--- 112,118 ----
extern char **environ;
static FILE *log;
+ static int debug_flag = 1;
char *safe_env_lst[] =
{
***************
*** 155,160 ****
--- 156,174 ----
NULL
};
+ typedef struct dir_list {
+ char *dir;
+ struct dir_list *next;
+ } dir_list;
+
+ typedef struct {
+ int user_dir_on;
+ char *user_dir_suff;
+ dir_list *dirs[2];
+ } suexec_attribs;
+
+ suexec_attribs attr;
+
static void err_output(const char *fmt, va_list ap)
{
***************
*** 194,199 ****
--- 208,226 ----
return;
}
+ void debug (const char *fmt,...) {
+ #ifdef LOG_EXEC
+ if (debug_flag) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_output(fmt, ap);
+ va_end(ap);
+ }
+ #endif /* LOG_EXEC */
+ return;
+ }
+
void clean_env()
{
char pathbuf[512];
***************
*** 231,236 ****
--- 258,443 ----
environ = cleanenv;
}
+ void load_setup_file () {
+ char pathbuf[512];
+ char linebuf[512];
+ int linecount = 0;
+ FILE *fp;
+ struct stat dir_info; /* directives directory info holder */
+ struct stat file_info; /* directives file info holder */
+
+ attr.user_dir_on = 0;
+ attr.user_dir_suff = 0;
+ attr.dirs[0] = attr.dirs[1] = 0;
+
+ /*
+ * Stat the cwd and verify it is a directory, or error out.
+ */
+ if (((lstat(DIRECTIVE_DIR, &dir_info)) != 0)
+ || !(S_ISDIR(dir_info.st_mode))) {
+ log_err("cannot stat directory: (%s)\n", DIRECTIVE_DIR);
+ exit(130);
+ }
+
+ /*
+ * Error out if DIRECTIVE_DIR is writable by others.
+ */
+ if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
+ log_err("directory is writable by others: (%s)\n", DIRECTIVE_DIR);
+ exit(131);
+ }
+
+ strcpy (pathbuf, DIRECTIVE_DIR);
+ strcat (pathbuf, "/");
+ strcat (pathbuf, DIRECTIVE_FILENAME);
+
+ /*
+ * Error out if we cannot stat the directives file.
+ */
+ if (((lstat(pathbuf, &file_info)) != 0) || (S_ISLNK(file_info.st_mode)))
{
+ log_err("cannot stat directives file: (%s)\n", pathbuf);
+ exit(132);
+ }
+
+ /*
+ * Error out if the directives file is writable by others.
+ */
+ if ((file_info.st_mode & S_IWOTH) || (file_info.st_mode & S_IWGRP)) {
+ log_err("file is writable by others: (%s)\n", pathbuf);
+ exit(133);
+ }
+
+ if ((fp = fopen (pathbuf, "r")) == 0) {
+ log_err("failed to open directives file: (%s)\n", pathbuf);
+ exit(134);
+ }
+
+ while (fgets (linebuf, 512, fp)) {
+ char *key = linebuf;
+ char *val;
+ char *cp;
+ int len = strlen (linebuf);
+
+ ++linecount;
+
+ /*
+ * Get rid of that trailing newline!
+ * If there isn't one, then the line is too long.
+ */
+ if (linebuf [len - 1] == '\n') {
+ linebuf [len - 1] = '\0';
+ } else {
+ log_err("line %d: line too long: (%s)\n", linecount, linebuf);
+ exit(135);
+ }
+
+ /*
+ * Directives may not extend across newline boundaries.
+ * Honor '#' signs as comment delimiters.
+ *
+ * First strip off all leading white space,
+ * determine the directive, isolate the value + trailing stuff,
+ * then step back from the tail getting rid of trailing
+ * comments and white space.
+ */
+ while (*key && isspace (*key)) {
+ ++key;
+ }
+ if (! *key || *key == '#') {
+ continue;
+ }
+ debug ("beginning of key: <%s>\n", key);
+
+ /*
+ * That should be the beginning of the directive.
+ * Find the first whitespace or ':' and set it null
+ * to isolate the directive.
+ */
+ cp = key;
+ while (*cp && *cp != ':' && ! isspace (*cp)) {
+ ++cp;
+ }
+ if (! *cp) {
+ log_err("line %d: end of directive not found: (%s)\n",
+ linecount, linebuf);
+ exit(136);
+ }
+ *cp = '\0';
+ debug ("finished key: <%s>\n", key);
+
+ /*
+ * Now find the beginning of the value, which is the
+ * next non-null, non-colon character.
+ */
+ ++cp;
+ while (*cp && (*cp == ':' || isspace (*cp))) {
+ ++cp;
+ }
+ if (! *cp) {
+ log_err("line %d: beginning of value not found: (%s)\n",
+ linecount, linebuf);
+ exit(137);
+ }
+ val = cp;
+ debug ("beginning of value: <%s>\n", val);
+
+ /*
+ * If there's a comment, get rid of it.
+ * Then strip off trailing whitespace.
+ */
+ if (cp = strchr (val, '#')) {
+ *cp = '\0';
+ }
+ cp = val + strlen (val) - 1;
+ while (cp > val && isspace (*cp)) {
+ *cp = '\0';
+ --cp;
+ }
+ debug ("val w/o trailing comments: <%s>\n", val);
+
+ /*
+ * Ok, it's a take.
+ * Directory directives get tacked onto the
+ * dirs list; the others go to their specific
+ * suexec_attribs field.
+ */
+
+ if (strcmp (key, "Directory") == 0) {
+ dir_list *dl;
+ if (! (dl = malloc (sizeof (dir_list)))) {
+ log_err("line %d: out of memory\n", linecount);
+ exit(138);
+ }
+ dl->next = 0;
+ if (! (dl->dir = strdup (val))) {
+ log_err("line %d: out of memory\n", linecount);
+ exit(139);
+ }
+ if (! attr.dirs[0]) {
+ attr.dirs[0] = attr.dirs[1] = dl;
+ } else {
+ attr.dirs[1]->next = dl;
+ attr.dirs[1] = dl;
+ }
+ } else if (strcmp (key, "UserDirectories") == 0) {
+ if (strcmp (val, "on") == 0) {
+ attr.user_dir_on = 1;
+ } else {
+ attr.user_dir_on = 0;
+ }
+ } else if (strcmp (key, "UserDirSuffix") == 0) {
+ if (! (attr.user_dir_suff = strdup (val))) {
+ log_err("line %d: out of memory\n", linecount);
+ exit(140);
+ }
+ } else {
+ log_err("line %d: unknown directive: (%s)\n",
+ linecount, key);
+ exit(141);
+ }
+ }
+ }
+
int main(int argc, char *argv[])
{
int userdir = 0; /* ~userdir flag */
***************
*** 249,254 ****
--- 456,462 ----
struct group *gr; /* group entry holder */
struct stat dir_info; /* directory info holder */
struct stat prg_info; /* program info holder */
+ int dirfound = 0;
***************
*** 264,269 ****
--- 472,479 ----
target_uname = argv[1];
target_gname = argv[2];
cmd = argv[3];
+ debug ("uname=<%s> gname=<%s> cmd=<%s>\n",
+ target_uname, target_gname, cmd);
/*
* Check existence/validity of the UID of the user
***************
*** 286,313 ****
}
/*
! * Check for a leading '/' (absolute path) in the command to be executed,
! * or attempts to back up out of the current directory,
* to protect against attacks. If any are
* found, error out. Naughty naughty crackers.
*/
! if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))
|| (strstr(cmd, "/../") != NULL)) {
log_err("invalid command (%s)\n", cmd);
exit(104);
}
/*
- * Check to see if this is a ~userdir request. If
- * so, set the flag, and remove the '~' from the
- * target username.
- */
- if (!strncmp("~", target_uname, 1)) {
- target_uname++;
- userdir = 1;
- }
-
- /*
* Error out if the target username is invalid.
*/
if ((pw = getpwnam(target_uname)) == NULL) {
--- 496,512 ----
}
/*
! * Check for attempts to back up out of the current directory,
* to protect against attacks. If any are
* found, error out. Naughty naughty crackers.
*/
! if ((!strncmp(cmd, "../", 3))
|| (strstr(cmd, "/../") != NULL)) {
log_err("invalid command (%s)\n", cmd);
exit(104);
}
/*
* Error out if the target username is invalid.
*/
if ((pw = getpwnam(target_uname)) == NULL) {
***************
*** 372,378 ****
* and setgid() to the target group. If unsuccessful, error out.
*/
if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
! log_err("failed to setgid (%ld: %s/%s)\n", gid, cwd, cmd);
exit(109);
}
--- 571,577 ----
* and setgid() to the target group. If unsuccessful, error out.
*/
if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
! log_err("failed to setgid (%ld: %s)\n", gid, cmd);
exit(109);
}
***************
*** 380,394 ****
* setuid() to the target user. Error out on fail.
*/
if ((setuid(uid)) != 0) {
! log_err("failed to setuid (%ld: %s/%s)\n", uid, cwd, cmd);
exit(110);
}
/*
! * Get the current working directory, as well as the proper
! * document root (dependant upon whether or not it is a
! * ~userdir request). Error out if we cannot get either one,
! * or if the current working directory is not in the docroot.
* Use chdir()s and getcwd()s to avoid problems with symlinked
* directories. Yuck.
*/
--- 579,590 ----
* setuid() to the target user. Error out on fail.
*/
if ((setuid(uid)) != 0) {
! log_err("failed to setuid (%ld: %s)\n", uid, cmd);
exit(110);
}
/*
! * Get the current working directory.
* Use chdir()s and getcwd()s to avoid problems with symlinked
* directories. Yuck.
*/
***************
*** 396,424 ****
log_err("cannot get current working directory\n");
exit(111);
}
!
! if (userdir) {
! if (((chdir(target_homedir)) != 0) ||
! ((chdir(USERDIR_SUFFIX)) != 0) ||
! ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
! ((chdir(cwd)) != 0)) {
! log_err("cannot get docroot information (%s)\n", target_homedir);
! exit(112);
! }
! }
! else {
! if (((chdir(DOC_ROOT)) != 0) ||
! ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
! ((chdir(cwd)) != 0)) {
! log_err("cannot get docroot information (%s)\n", DOC_ROOT);
! exit(113);
! }
! }
!
! if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
! log_err("command not in docroot (%s/%s)\n", cwd, cmd);
! exit(114);
! }
/*
* Stat the cwd and verify it is a directory, or error out.
--- 592,598 ----
log_err("cannot get current working directory\n");
exit(111);
}
! debug ("cwd=<%s>\n", cwd);
/*
* Stat the cwd and verify it is a directory, or error out.
***************
*** 434,439 ****
--- 608,664 ----
if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
log_err("directory is writable by others: (%s)\n", cwd);
exit(116);
+ }
+
+ /*
+ * Load the setup file as late as possible.
+ */
+ load_setup_file ();
+
+ /*
+ * Search the directory lists to determine if the command
+ * is in a legal directory. The current directory is the
+ * directory where the command is supposed to be. If the
+ * current directory is the same as, or a subdirectory of,
+ * one of the list directories, then it's ok to exec it.
+ */
+
+ if (attr.user_dir_on) {
+ char td[AP_MAXPATH]; /* test directory */
+ /*
+ * Construct the home directory for the specified
+ * user.
+ */
+ strcpy (td, target_homedir);
+ strcat (td, "/");
+ strcat (td, attr.user_dir_suff);
+ debug ("check cwd=<%s> against user dir <%s>\n", cwd, td);
+ if (strncmp (cwd, td, strlen (td)) == 0) {
+ ++dirfound;
+ }
+ }
+ if (! dirfound) {
+ dir_list *dl = attr.dirs[0];
+ /*
+ * Step through the directories which suexec is
+ * allowed to work in, looking for a prefix match
+ * with the current directory.
+ */
+ while (dl) {
+ debug ("check cwd=<%s> against <%s>\n", cwd, dl->dir);
+ if (strncmp (cwd, dl->dir, strlen (dl->dir)) == 0) {
+ ++dirfound;
+ break;
+ } else {
+ dl = dl->next;
+ }
+ }
+ }
+
+ if (! dirfound) {
+ log_err("unable to validate directory (%s) for user (%s), "
+ "group (%s), cmd (%s)\n", cwd, target_uname, target_gname, cmd);
+ exit(121);
}
/*
diff -c --exclude=*.[oa] --recursive apache_1.3b3/src/support/suexec.h
a13b3/src/support/suexec.h
*** apache_1.3b3/src/support/suexec.h Sat Oct 25 16:35:20 1997
--- a13b3/src/support/suexec.h Wed Feb 4 23:45:28 1998
***************
*** 65,71 ****
* this program.
*/
#ifndef HTTPD_USER
! #define HTTPD_USER "www"
#endif
/*
--- 65,71 ----
* this program.
*/
#ifndef HTTPD_USER
! #define HTTPD_USER "nobody"
#endif
/*
***************
*** 115,121 ****
* debugging purposes.
*/
#ifndef LOG_EXEC
! #define LOG_EXEC "/usr/local/apache/logs/cgi.log" /* Need me? */
#endif
/*
--- 115,121 ----
* debugging purposes.
*/
#ifndef LOG_EXEC
! #define LOG_EXEC "/users/src/a13b3/logs/cgi.log" /* Need me? */
#endif
/*
***************
*** 133,138 ****
--- 133,154 ----
*/
#ifndef SAFE_PATH
#define SAFE_PATH "/usr/local/bin:/usr/bin:/bin"
+ #endif
+
+ /*
+ * DIRECTIVE_DIR -- Path to the directives file.
+ *
+ */
+ #ifndef DIRECTIVE_DIR
+ #define DIRECTIVE_DIR "/users/src/a13b3/conf"
+ #endif
+
+ /*
+ * DIRECTIVE_FILENAME -- Name of the directives file.
+ *
+ */
+ #ifndef DIRECTIVE_FILENAME
+ #define DIRECTIVE_FILENAME "suexec.conf"
#endif
#endif /* _SUEXEC_H */