home *** CD-ROM | disk | FTP | other *** search
- From: Gary Shea <shea@gtsdesign.com>
- To: dgaudet@arctic.com
- Cc: pvanhaes@be.oracle.com, shea@gtsdesign.com, apbugs@apache.org
- 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 (pvanhaes@be.oracle.com)
- 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 */
-
-