This is the patch to Apache's suexec program described on page 344 of "Web Security: A Step-by-Step Reference Guide" by Lincoln Stein. After application, it relaxes suexec's security checks in the following ways: 1. If the CGI script is run from a user-supported directory (one that uses the ~username notation), suexec behaves exactly as it always did. 2. If the CGI script is run on a script in a virtual host, suexec will allow the script to be run even if it is not owned by the virtual host's effective user (set with the User directive). It will refuse to run if the script or the directory it resides in is writable by the effective user. The rationale for this change is that it is not necessarily a good idea to run CGI scripts with the same permissions as their author, because this opens the door to Web site vandalism. By running the CGI script as a different user, the person who maintains the virtual site is protected against interference by his own scripts. The suggested configuration is to create two user accounts for each virtual host. The first account is designed for use by the person responsible for maintaining the virtual site and has full write permissions for the site's document tree. The second account is designed for use only by the virtual host's CGI scripts. It should have no particular privileges, and particularly no write permissions for the document root or other parts of the site. For more information about suexec, see the Apache Web server documentation at http://www.apache.org/ Apply this patch as follows: > cd apache-X.X/src/support ; enter apache distribution > patch < /path/to/this/file/suexec_patch.txt suexec is actually rather limited in the attacks that it protects against. If you are looking for a more thorough type of wrapper script, you should check out the sbox wrapper. This script, written by myself, goes further than other wrappers by performing a chroot to a restricted part of the directory tree, establishing limits on resource usage, and by changing its permissions to an unprivileged user. You can find it at: http://www.genome.wi.mit.edu/~lstein/sbox/ Lincoln Stein January 4, 1998 *** /home/lstein/ccod/apache_1.3b3/src/support/suexec.c Wed Oct 22 16:30:46 1997 --- ./suexec.c Sat Jan 3 17:43:56 1998 *************** *** 67,72 **** --- 67,74 ---- * */ + /* patched by Lincoln Stein 1/3/98 to allow to run with other than + author's permissions */ #include "suexec.h" *************** *** 249,255 **** struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ ! /* --- 251,258 ---- struct group *gr; /* group entry holder */ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ ! int anum; /* number of groups */ ! gid_t gary[32]; /* array of groups */ /* *************** *** 431,437 **** /* * Error out if cwd 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", cwd); exit(116); } --- 434,440 ---- /* * Error out if cwd is writable by others. */ ! if ((dir_info.st_mode & S_IWOTH) || (userdir && (dir_info.st_mode & S_IWGRP))) { log_err("directory is writable by others: (%s)\n", cwd); exit(116); } *************** *** 446,453 **** /* * Error out if the program is writable by others. */ ! if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { log_err("file is writable by others: (%s/%s)\n", cwd, cmd); exit(118); } --- 449,457 ---- /* * Error out if the program is writable by others. + * Remove group restriction if virtual hosts in use. */ ! if ((prg_info.st_mode & S_IWOTH) || (userdir && (prg_info.st_mode & S_IWGRP))) { log_err("file is writable by others: (%s/%s)\n", cwd, cmd); exit(118); } *************** *** 463,478 **** /* * Error out if the target name/group is different from * the name/group of the cwd or the program. */ ! if ((uid != dir_info.st_uid) || ! (gid != dir_info.st_gid) || ! (uid != prg_info.st_uid) || ! (gid != prg_info.st_gid)) { log_err("target uid/gid (%ld/%ld) mismatch with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); } clean_env(); --- 467,530 ---- /* * Error out if the target name/group is different from * the name/group of the cwd or the program. + * + * LDS - this has been modified because it forces a situation in which the + * script can modify itself. Instead we error out if the script is self-modifiable. */ ! if (userdir) { /* User directories force script to be owned by the effective user... */ ! if ((uid != dir_info.st_uid) || ! (gid != dir_info.st_gid) || ! (uid != prg_info.st_uid) || ! (gid != prg_info.st_gid)) { log_err("target uid/gid (%ld/%ld) mismatch with directory (%ld/%ld) or program (%ld/%ld)\n", uid, gid, dir_info.st_uid, dir_info.st_gid, prg_info.st_uid, prg_info.st_gid); exit(120); + } + } else { + /* + * .... but for virtual hosts, don't want this situation. + * error out if directory or program are writable by current user + */ + if ((dir_info.st_mode & S_IWUSR) && (dir_info.st_uid == uid)) + { + log_err("target uid (%ld) should not be able to write to directory (%ld)\n", + uid,dir_info.st_uid); + exit(120); + } + if ((prg_info.st_mode & S_IWUSR) && (prg_info.st_uid == uid)) + { + log_err("target uid (%ld) should not be able to write to program file (%ld)\n", + uid,prg_info.st_uid); + exit(121); + } + + /* + * error out if directory or program are writable by current group + */ + if (dir_info.st_mode & S_IWGRP ) { + anum = getgroups(32,gary); + while (--anum >= 0) { + if (gary[anum] == dir_info.st_gid) + { + log_err("target uid (%ld) should not have group (%ld) write privileges to directory\n", + uid,gary[anum]); + exit(122); + } + } + } + if (prg_info.st_mode & S_IWGRP) { + anum = getgroups(32,gary); + while (--anum >= 0) { + if (gary[anum] == prg_info.st_gid) + { + log_err("target uid (%ld) should not have group (%ld) write privileges to program file\n", + uid,gary[anum]); + exit(123); + } + } + } } clean_env();