randy 97/01/12 11:20:35
Modified: src CHANGES
Log:
Add suexec CHANGES
Revision Changes Path
1.118 +11 -0 apache/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.117
retrieving revision 1.118
diff -C3 -r1.117 -r1.118
*** CHANGES 1997/01/12 16:58:00 1.117
--- CHANGES 1997/01/12 19:18:46 1.118
***************
*** 1,5 ****
--- 1,16 ----
Changes with Apache 1.2b5
+ *) Several security enhancements to suexec wrapper. It is _highly_
+ recommended that previously installed versions of the wrapper
+ be replaced with this version. [Randy Terbush, Jason Dour]
+
+ - ~user execution now properly restricted to ~user's home
+ directory and below.
+ - execution restricted to UID/GID > 100
+ - restrict passed environment to known variables
+ - call setgid() before initgroups() (portability fix)
+ - remove use of setenv() (portability fix)
+
*) Add HTTP/1.0 response forcing. [Ben Laurie]
*) Add access control via environment variables. [Ben Laurie]
Modified: support suexec.c suexec.h
Log:
Add serveral security enhancements.
- ~user execution now properly restricted to ~user's home
directory and below.
- execution restricted to UID/GID > 100
- restrict passed environment to known variables
- call setgid() before initgroups() (portability fix)
- remove use of setenv() (portability fix)
Reviewed-by: Marc Slemko, Jason Dour, Randy Terbush
Revision Changes Path
1.11 +99 -18 apache/support/suexec.c
Index: suexec.c
===================================================================
RCS file: /export/home/cvs/apache/support/suexec.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -C3 -r1.10 -r1.11
*** suexec.c 1997/01/01 18:26:17 1.10
--- suexec.c 1997/01/12 19:20:33 1.11
***************
*** 81,89 ****
--- 81,132 ----
#include <time.h>
#include <sys/stat.h>
+ #define CLEAN_ENV_BUF 256
+ extern char **environ;
static FILE *log;
+ char *safe_env_lst[] =
+ {
+ "AUTH_TYPE",
+ "CONTENT_LENGTH",
+ "CONTENT_TYPE",
+ "DATE_GMT",
+ "DATE_LOCAL",
+ "DOCUMENT_NAME",
+ "DOCUMENT_PATH_INFO",
+ "DOCUMENT_ROOT",
+ "DOCUMENT_URI",
+ "FILEPATH_INFO",
+ "GATEWAY_INTERFACE",
+ "LAST_MODIFIED",
+ "PATH_INFO",
+ "PATH_TRANSLATED",
+ "QUERY_STRING",
+ "QUERY_STRING_UNESCAPED",
+ "REMOTE_ADDR",
+ "REMOTE_HOST",
+ "REMOTE_IDENT",
+ "REMOTE_PORT",
+ "REMOTE_USER",
+ "REDIRECT_QUERY_STRING",
+ "REDIRECT_STATUS",
+ "REDIRECT_URL",
+ "REQUEST_METHOD",
+ "SCRIPT_FILENAME",
+ "SCRIPT_NAME",
+ "SCRIPT_URI",
+ "SCRIPT_URL",
+ "SERVER_ADMIN",
+ "SERVER_NAME",
+ "SERVER_PORT",
+ "SERVER_PROTOCOL",
+ "SERVER_SOFTWARE",
+ "USER_NAME",
+ NULL
+ };
+
+
static void err_output(const char *fmt, va_list ap)
{
time_t timevar;
***************
*** 120,128 ****
return;
}
! int main(int argc, char *argv[], char **env)
{
- int doclen; /* length of the docroot */
int userdir = 0; /* ~userdir flag */
uid_t uid; /* user information */
gid_t gid; /* target group placeholder */
--- 163,208 ----
return;
}
! void clean_env()
! {
! char pathbuf[512];
! char **cleanenv;
! char **ep;
! int cidx = 0;
! int idx;
!
!
! if ((cleanenv = (char **)malloc(CLEAN_ENV_BUF * (sizeof(char *)))) ==
NULL) {
! log_err("failed to malloc env mem\n");
! exit(120);
! }
!
! for (ep = environ; *ep; ep++) {
! if (!strncmp(*ep, "HTTP_", 5)) {
! cleanenv[cidx] = *ep;
! cidx++;
! }
! else {
! for (idx = 0; safe_env_lst[idx]; idx++) {
! if (!strncmp(*ep, safe_env_lst[idx],
strlen(safe_env_lst[idx]))) {
! cleanenv[cidx] = *ep;
! cidx++;
! break;
! }
! }
! }
! }
!
! sprintf(pathbuf, "PATH=%s", SAFE_PATH);
! cleanenv[cidx] = pathbuf;
! cleanenv[++cidx] = NULL;
!
! environ = cleanenv;
! free(cleanenv);
! }
!
! int main(int argc, char *argv[])
{
int userdir = 0; /* ~userdir flag */
uid_t uid; /* user information */
gid_t gid; /* target group placeholder */
***************
*** 211,217 ****
/*
* 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.
--- 291,297 ----
/*
* 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.
***************
*** 223,228 ****
--- 303,309 ----
if (userdir) {
if (((chdir(pw->pw_dir)) != 0) ||
+ ((chdir(USERDIR_SUFFIX)) != 0) ||
((getcwd(dwd, MAXPATHLEN)) == NULL) ||
((chdir(cwd)) != 0))
{
***************
*** 239,247 ****
exit(108);
}
}
!
! doclen = strlen(dwd);
! if ((strncmp(cwd, dwd, doclen)) != 0) {
log_err("command not in docroot (%s/%s)\n", cwd, cmd);
exit(109);
}
--- 320,327 ----
exit(108);
}
}
!
! if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
log_err("command not in docroot (%s/%s)\n", cwd, cmd);
exit(109);
}
***************
*** 303,320 ****
}
/*
! * Error out if attempt is made to execute as root. Tsk tsk.
*/
! if (pw->pw_uid == 0) {
! log_err("cannot run as uid 0 (%s)\n", cmd);
exit(116);
}
/*
! * Error out if attempt is made to execute as root group. Tsk tsk.
*/
! if (gr->gr_gid == 0) {
! log_err("cannot run as gid 0 (%s)\n", cmd);
exit(117);
}
--- 383,404 ----
}
/*
! * Error out if attempt is made to execute as root or as
! * a UID less than UID_MIN. Tsk tsk.
*/
! if ((pw->pw_uid == 0) ||
! (pw->pw_uid < UID_MIN)) {
! log_err("cannot run as forbidden uid (%d/%s)\n", pw->pw_uid, cmd);
exit(116);
}
/*
! * Error out if attempt is made to execute as root group
! * or as a GID less than GID_MIN. Tsk tsk.
*/
! if ((gr->gr_gid == 0) ||
! (gr->gr_gid < GID_MIN)) {
! log_err("cannot run as forbidden gid (%d/%s)\n", gr->gr_gid, cmd);
exit(117);
}
***************
*** 333,339 ****
*/
uid = pw->pw_uid;
gid = gr->gr_gid;
! if ((initgroups(target_uname,gid) != 0) || ((setgid(gid)) != 0)) {
log_err("failed to setgid (%ld: %s/%s)\n", gid, cwd, cmd);
exit(118);
}
--- 417,423 ----
*/
uid = pw->pw_uid;
gid = gr->gr_gid;
! if (((setgid(gid)) != 0) || (initgroups(pw->pw_name,gid) != 0)) {
log_err("failed to setgid (%ld: %s/%s)\n", gid, cwd, cmd);
exit(118);
}
***************
*** 346,360 ****
exit(119);
}
! if ((setenv("PATH", SAFE_PATH, 1)) != 0) {
! log_err("cannot reset environment PATH\n");
! exit(120);
! }
/*
* Execute the command, replacing our image with its own.
*/
! execve(cmd, &argv[3], env);
/*
* (I can't help myself...sorry.)
--- 430,441 ----
exit(119);
}
! clean_env();
/*
* Execute the command, replacing our image with its own.
*/
! execv(cmd, argv);
/*
* (I can't help myself...sorry.)
1.7 +30 -0 apache/support/suexec.h
Index: suexec.h
===================================================================
RCS file: /export/home/cvs/apache/support/suexec.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -C3 -r1.6 -r1.7
*** suexec.h 1997/01/01 18:26:18 1.6
--- suexec.h 1997/01/12 19:20:34 1.7
***************
*** 68,73 ****
--- 68,103 ----
#endif
/*
+ * UID_MIN -- Define this as the lowest UID allowed to be a target user
+ * for suEXEC. For most systems, 500 or 100 is common.
+ */
+ #ifndef UID_MIN
+ #define UID_MIN 500
+ #endif
+
+ /*
+ * GID_MIN -- Define this as the lowest GID allowed to be a target group
+ * for suEXEC. For most systems, 100 is common.
+ */
+ #ifndef GID_MIN
+ #define GID_MIN 100
+ #endif
+
+ /*
+ * USERDIR_SUFFIX -- Define to be the same as the UserDir in the conf
+ * file. If you have VirtualHosts with a different
+ * UserDir for each, you will need to define them to
+ * all reside in one parent directory; then name that
+ * parent directory here. IF THIS IS NOT DEFINED
+ * PROPERLY, ~USERDIR CGI REQUESTS WILL NOT WORK!
+ * See the suEXEC documentation for more detailed
+ * information.
+ */
+ #ifndef USERDIR_SUFFIX
+ #define USERDIR_SUFFIX "public_html"
+ #endif
+
+ /*
* LOG_EXEC -- Define this as a filename if you want all suEXEC
* transactions and errors logged for auditing and
* debugging purposes.