# scripts.mit.edu httpd suexec patch # Copyright (C) 2006, 2007, 2008 Jeff Arnold , # Joe Presbrey , # Anders Kaseorg , # Geoffrey Thomas # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # # See /COPYRIGHT in this repository for more information. # --- httpd-2.2.2/support/Makefile.in.old 2005-07-06 19:15:34.000000000 -0400 +++ httpd-2.2.2/support/Makefile.in 2007-01-20 17:12:51.000000000 -0500 @@ -60,7 +60,7 @@ suexec_OBJECTS = suexec.lo suexec: $(suexec_OBJECTS) - $(LINK) $(suexec_OBJECTS) + $(LINK) -lselinux $(suexec_OBJECTS) htcacheclean_OBJECTS = htcacheclean.lo htcacheclean: $(htcacheclean_OBJECTS) --- httpd-2.2.2/configure.in.old 2007-07-17 10:48:25.000000000 -0400 +++ httpd-2.2.2/configure.in 2008-08-29 08:15:41.000000000 -0400 @@ -559,6 +559,10 @@ APACHE_HELP_STRING(--with-suexec-userdir,User subdirectory),[ AC_DEFINE_UNQUOTED(AP_USERDIR_SUFFIX, "$withval", [User subdirectory] ) ] ) +AC_ARG_WITH(suexec-trusteddir, +APACHE_HELP_STRING(--with-suexec-trusteddir,Trusted SuExec directory),[ + AC_DEFINE_UNQUOTED(AP_TRUSTED_DIRECTORY, "$withval", [Trusted SuExec directory] ) ] ) + AC_ARG_WITH(suexec-docroot, APACHE_HELP_STRING(--with-suexec-docroot,SuExec root directory),[ AC_DEFINE_UNQUOTED(AP_DOC_ROOT, "$withval", [SuExec root directory] ) ] ) --- httpd-2.2.11/support/suexec.c.old 2008-11-30 10:47:31.000000000 -0500 +++ httpd-2.2.11/support/suexec.c 2009-06-08 09:02:17.000000000 -0400 @@ -30,6 +30,9 @@ * */ +#define STATIC_CAT_PATH "/usr/local/bin/static-cat" +#define PHP_PATH "/usr/bin/php-cgi" + #include "apr.h" #include "ap_config.h" #include "suexec.h" @@ -46,6 +49,7 @@ #include #include #include +#include #ifdef HAVE_PWD_H #include @@ -95,6 +99,7 @@ { /* variable name starts with */ "HTTP_", + "HTTPS_", "SSL_", /* variable name is */ @@ -245,9 +250,67 @@ environ = cleanenv; } +static const char *static_extensions[] = { + "html", + "css", + "gif", + "jpg", + "png", + "htm", + "jpeg", + "js", + "ico", + "xml", + "xsl", + "tiff", + "tif", + "tgz", + "tar", + "jar", + "zip", + "pdf", + "ps", + "doc", + "xls", + "ppt", + "swf", + "mp3", + "mov", + "wmv", + "mpg", + "mpeg", + "avi", + "il", + "JPG", + "xhtml", + "svg", + "xaml", + "xap", + NULL +}; + +static int is_static_extension(const char *file) +{ + const char *extension = strrchr(file, '.'); + const char **p; + if (extension == NULL) return 0; + for (p = static_extensions; *p; ++p) { + if (strcmp(extension + 1, *p) == 0) return 1; + } + return 0; +} + +static int is_php_extension(const char *file) +{ + const char *extension = strrchr(file, '.'); + if (extension == NULL) return 0; + return strcmp(extension + 1, "php") == 0; +} + int main(int argc, char *argv[]) { int userdir = 0; /* ~userdir flag */ + int trusteddir = 0; /* TRUSTED_DIRECTORY flag */ uid_t uid; /* user information */ gid_t gid; /* target group placeholder */ char *target_uname; /* target user name */ @@ -268,6 +331,7 @@ * Start with a "clean" environment */ clean_env(); + setenv("JAVA_TOOL_OPTIONS", "-Xmx128M", 1); /* scripts.mit.edu local hack */ prog = argv[0]; /* @@ -350,6 +414,20 @@ #endif /*_OSD_POSIX*/ /* + * First check if this is an absolute path to the directory + * of trusted executables. These are supposed to be security + * audited to check parameters and validity on their own... + */ + if (strstr(cmd, AP_TRUSTED_DIRECTORY) == cmd) { + if (strstr(cmd, "/../") != NULL) { + log_err("invalid command (%s)\n", cmd); + exit(104); + } + trusteddir = 1; + goto TRUSTED_DIRECTORY; + } + + /* * 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 @@ -371,6 +449,7 @@ userdir = 1; } +TRUSTED_DIRECTORY: /* * Error out if the target username is invalid. */ @@ -452,7 +531,7 @@ * Error out if attempt is made to execute as root or as * a UID less than AP_UID_MIN. Tsk tsk. */ - if ((uid == 0) || (uid < AP_UID_MIN)) { + if ((uid == 0) || (uid < AP_UID_MIN && uid != 102)) { log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd); exit(107); } @@ -484,6 +563,21 @@ log_err("failed to setuid (%ld: %s)\n", uid, cmd); exit(110); } + if (is_selinux_enabled()) { + if (uid == 102) { + if (setexeccon("system_u:system_r:signup_t:s0") == -1) { + log_err("failed to setexeccon (%ld: %s) to signup_t\n", uid, cmd); + exit(201); + } + } else { + if (setexeccon("user_u:user_r:user_t:s0") == -1) { + log_err("failed to setexeccon (%ld: %s) to user_t\n", uid, cmd); + exit(202); + } + } + } + + setenv("HOME", target_homedir, 1); /* * Get the current working directory, as well as the proper @@ -506,6 +600,21 @@ log_err("cannot get docroot information (%s)\n", target_homedir); exit(112); } + size_t expected_len = strlen(target_homedir)+1+strlen(AP_USERDIR_SUFFIX)+1; + char *expected = malloc(expected_len); + snprintf(expected, expected_len, "%s/%s", target_homedir, AP_USERDIR_SUFFIX); + if (strncmp(cwd, expected, expected_len-1) != 0) { + log_err("error: file's directory not a subdirectory of user's home directory (%s, %s)\n", cwd, expected); + exit(114); + } + } + else if (trusteddir) { + if (((chdir(AP_TRUSTED_DIRECTORY)) != 0) || + ((getcwd(dwd, AP_MAXPATH)) == NULL) | + ((chdir(cwd)) != 0)) { + log_err("cannot get docroot information (%s)\n", AP_TRUSTED_DIRECTORY); + exit(112); + } } else { if (((chdir(AP_DOC_ROOT)) != 0) || @@ -532,15 +641,17 @@ /* * Error out if cwd is writable by others. */ +#if 0 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); } +#endif /* * Error out if we cannot stat the program. */ - if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { + if (((lstat(cmd, &prg_info)) != 0) /*|| (S_ISLNK(prg_info.st_mode))*/) { log_err("cannot stat program: (%s)\n", cmd); exit(117); } @@ -548,10 +659,12 @@ /* * Error out if the program is writable by others. */ +#if 0 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); } +#endif /* * Error out if the file is setuid or setgid. @@ -565,6 +678,7 @@ * Error out if the target name/group is different from * the name/group of the cwd or the program. */ +#if 0 if ((uid != dir_info.st_uid) || (gid != dir_info.st_gid) || (uid != prg_info.st_uid) || @@ -576,16 +690,33 @@ prg_info.st_uid, prg_info.st_gid); exit(120); } +#endif /* * Error out if the program is not executable for the user. * Otherwise, she won't find any error in the logs except for * "[error] Premature end of script headers: ..." */ - if (!(prg_info.st_mode & S_IXUSR)) { + if (!is_static_extension(cmd) && !is_php_extension(cmd) && + !(prg_info.st_mode & S_IXUSR)) { log_err("file has no execute permission: (%s/%s)\n", cwd, cmd); exit(121); } + if (is_static_extension(cmd)) { + argv[2] = STATIC_CAT_PATH; + execv(STATIC_CAT_PATH, &argv[2]); + log_err("(%d)%s: static_cat exec failed (%s)\n", errno, strerror(errno), argv[2]); + exit(255); + } + if (is_php_extension(cmd)) { + setenv("PHPRC", ".", 1); + argv[1] = PHP_PATH; + argv[2] = "-f"; + execv(PHP_PATH, &argv[1]); + log_err("(%d)%s: php exec failed (%s)\n", errno, strerror(errno), argv[2]); + exit(255); + } + #ifdef AP_SUEXEC_UMASK /* * umask() uses inverse logic; bits are CLEAR for allowed access.