source: trunk/server/common/patches/httpd-suexec-scripts.patch @ 2621

Last change on this file since 2621 was 2591, checked in by achernya, 10 years ago
Reintegrate fc20-dev into trunk
File size: 9.7 KB
RevLine 
[2591]1From 427d432a56df94d69a11cc438b08adb070615005 Mon Sep 17 00:00:00 2001
2From: Alexander Chernyakhovsky <achernya@mit.edu>
3Date: Fri, 3 May 2013 21:38:58 -0400
4Subject: [PATCH] Add scripts-specific support to suexec
[103]5
[2591]6This patch make suexec aware of static-cat, Scripts' tool to serve
7static content out of AFS.  Specifically, this introduces a whitelist
8of extensions for which suexec is supposed to invoke static-cat as a
9content-handler.
[103]10
[2591]11Additionally, this patch also sets JAVA_TOOL_OPTIONS, to allow the JVM
12to start up in Scripts' limited memory environment.
13
14Furthermore, this patch deals with some of suexec's paranoia being
15incorrect in an AFS world, by ignoring some of the irrelevant stat
16results.
17
18Finally, add support for invoking php-cgi for php files, in a safe
19manner that will strip arguments passed by Apache to php-cgi.
20---
21 configure.in     |   4 ++
22 support/suexec.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
23 2 files changed, 173 insertions(+), 3 deletions(-)
24
25diff --git a/configure.in b/configure.in
26index 811aace..a95349f 100644
27--- a/configure.in
28+++ b/configure.in
29@@ -721,6 +721,10 @@ AC_ARG_WITH(suexec-userdir,
[823]30 APACHE_HELP_STRING(--with-suexec-userdir,User subdirectory),[
31   AC_DEFINE_UNQUOTED(AP_USERDIR_SUFFIX, "$withval", [User subdirectory] ) ] )
32 
33+AC_ARG_WITH(suexec-trusteddir,
34+APACHE_HELP_STRING(--with-suexec-trusteddir,Trusted SuExec directory),[
35+  AC_DEFINE_UNQUOTED(AP_TRUSTED_DIRECTORY, "$withval", [Trusted SuExec directory] ) ] )
36+
37 AC_ARG_WITH(suexec-docroot,
38 APACHE_HELP_STRING(--with-suexec-docroot,SuExec root directory),[
39   AC_DEFINE_UNQUOTED(AP_DOC_ROOT, "$withval", [SuExec root directory] ) ] )
[2591]40diff --git a/support/suexec.c b/support/suexec.c
41index 32e7320..3a4d802 100644
42--- a/support/suexec.c
43+++ b/support/suexec.c
[842]44@@ -30,6 +30,9 @@
[298]45  *
46  */
47 
[1590]48+#define STATIC_CAT_PATH "/usr/bin/static-cat"
[842]49+#define PHP_PATH "/usr/bin/php-cgi"
[298]50+
51 #include "apr.h"
52 #include "ap_config.h"
53 #include "suexec.h"
[2591]54@@ -92,6 +95,7 @@ static const char *const safe_env_lst[] =
[1]55 {
56     /* variable name starts with */
57     "HTTP_",
58+    "HTTPS_",
59     "SSL_",
60 
61     /* variable name is */
[2591]62@@ -268,9 +272,108 @@ static void clean_env(void)
[298]63     environ = cleanenv;
64 }
65 
66+static const char *static_extensions[] = {
67+    "html",
68+    "css",
69+    "gif",
70+    "jpg",
71+    "png",
72+    "htm",
73+    "jpeg",
74+    "js",
75+    "ico",
76+    "xml",
77+    "xsl",
78+    "tiff",
79+    "tif",
80+    "tgz",
81+    "tar",
82+    "jar",
83+    "zip",
84+    "pdf",
85+    "ps",
86+    "doc",
87+    "xls",
88+    "ppt",
[1877]89+    "dot",
90+    "docx",
91+    "dotx",
92+    "docm",
93+    "dotm",
94+    "xlt",
95+    "xla",
96+    "xlsx",
97+    "xltx",
98+    "xlsm",
99+    "xltm",
100+    "xlam",
101+    "xlsb",
102+    "pot",
103+    "pps",
104+    "ppa",
105+    "pptx",
106+    "potx",
107+    "ppsx",
108+    "ppam",
109+    "pptm",
110+    "potm",
111+    "ppsm",
[298]112+    "swf",
113+    "mp3",
114+    "mov",
115+    "wmv",
116+    "mpg",
117+    "mpeg",
118+    "avi",
119+    "il",
[315]120+    "xhtml",
[618]121+    "svg",
[944]122+    "xaml",
123+    "xap",
[1464]124+    "wav",
125+    "mid",
126+    "midi",
[1785]127+    "ttf",
128+    "otf",
[1877]129+    "odc",
130+    "odb",
131+    "odf",
132+    "odg",
133+    "otg",
134+    "odi",
135+    "odp",
136+    "otp",
137+    "ods",
138+    "ots",
139+    "odt",
140+    "odm",
141+    "ott",
142+    "oth",
[298]143+    NULL
144+};
145+
146+static int is_static_extension(const char *file)
147+{
148+    const char *extension = strrchr(file, '.');
149+    const char **p;
150+    if (extension == NULL) return 0;
151+    for (p = static_extensions; *p; ++p) {
[1464]152+        if (strcasecmp(extension + 1, *p) == 0) return 1;
[298]153+    }
154+    return 0;
155+}
156+
[842]157+static int is_php_extension(const char *file)
158+{
159+    const char *extension = strrchr(file, '.');
160+    if (extension == NULL) return 0;
161+    return strcmp(extension + 1, "php") == 0;
162+}
163+
[298]164 int main(int argc, char *argv[])
165 {
166     int userdir = 0;        /* ~userdir flag             */
[823]167+    int trusteddir = 0;     /* TRUSTED_DIRECTORY flag    */
168     uid_t uid;              /* user information          */
169     gid_t gid;              /* target group placeholder  */
170     char *target_uname;     /* target user name          */
[2591]171@@ -290,6 +393,7 @@ int main(int argc, char *argv[])
[1169]172      * Start with a "clean" environment
173      */
174     clean_env();
175+    setenv("JAVA_TOOL_OPTIONS", "-Xmx128M", 1); /* scripts.mit.edu local hack */
[1259]176 
[1169]177     /*
[2591]178      * Check existence/validity of the UID of the user
179@@ -373,6 +477,20 @@ int main(int argc, char *argv[])
[823]180 #endif /*_OSD_POSIX*/
181 
182     /*
183+     * First check if this is an absolute path to the directory
184+     * of trusted executables. These are supposed to be security
185+     * audited to check parameters and validity on their own...
186+     */
187+    if (strstr(cmd, AP_TRUSTED_DIRECTORY) == cmd) {
188+        if (strstr(cmd, "/../") != NULL) {
189+            log_err("invalid command (%s)\n", cmd);
190+            exit(104);
191+        }
192+        trusteddir = 1;
193+        goto TRUSTED_DIRECTORY;
194+    }
195+
196+    /*
197      * Check for a leading '/' (absolute path) in the command to be executed,
198      * or attempts to back up out of the current directory,
199      * to protect against attacks.  If any are
[2591]200@@ -394,6 +512,7 @@ int main(int argc, char *argv[])
[823]201         userdir = 1;
202     }
203 
204+TRUSTED_DIRECTORY:
205     /*
206      * Error out if the target username is invalid.
207      */
[2591]208@@ -482,7 +601,7 @@ int main(int argc, char *argv[])
[103]209      * Error out if attempt is made to execute as root or as
210      * a UID less than AP_UID_MIN.  Tsk tsk.
211      */
212-    if ((uid == 0) || (uid < AP_UID_MIN)) {
[1474]213+    if ((uid == 0) || (uid < AP_UID_MIN && uid != 102)) { /* uid 102 = signup  */
[2591]214         log_err("cannot run as forbidden uid (%lu/%s)\n", (unsigned long)uid, cmd);
[103]215         exit(107);
216     }
[2591]217@@ -514,6 +633,7 @@ int main(int argc, char *argv[])
218         log_err("failed to setuid (%lu: %s)\n", (unsigned long)uid, cmd);
[103]219         exit(110);
220     }
[908]221+    setenv("HOME", target_homedir, 1);
[103]222 
223     /*
224      * Get the current working directory, as well as the proper
[2591]225@@ -536,6 +656,21 @@ int main(int argc, char *argv[])
[823]226             log_err("cannot get docroot information (%s)\n", target_homedir);
227             exit(112);
[1]228         }
[823]229+        size_t expected_len = strlen(target_homedir)+1+strlen(AP_USERDIR_SUFFIX)+1;
230+        char *expected = malloc(expected_len);
231+        snprintf(expected, expected_len, "%s/%s", target_homedir, AP_USERDIR_SUFFIX);
232+        if (strncmp(cwd, expected, expected_len-1) != 0) {
233+            log_err("error: file's directory not a subdirectory of user's home directory (%s, %s)\n", cwd, expected);
234+            exit(114);
235+        }
236+    }
237+    else if (trusteddir) {
238+        if (((chdir(AP_TRUSTED_DIRECTORY)) != 0) ||
239+            ((getcwd(dwd, AP_MAXPATH)) == NULL) |
240+            ((chdir(cwd)) != 0)) {
241+            log_err("cannot get docroot information (%s)\n", AP_TRUSTED_DIRECTORY);
242+            exit(112);
243+        }
[1]244     }
[823]245     else {
246         if (((chdir(AP_DOC_ROOT)) != 0) ||
[2591]247@@ -562,15 +697,17 @@ int main(int argc, char *argv[])
[1]248     /*
249      * Error out if cwd is writable by others.
250      */
251+#if 0
252     if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
253         log_err("directory is writable by others: (%s)\n", cwd);
254         exit(116);
255     }
256+#endif
257 
258     /*
259      * Error out if we cannot stat the program.
260      */
261-    if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) {
262+    if (((lstat(cmd, &prg_info)) != 0) /*|| (S_ISLNK(prg_info.st_mode))*/) {
263         log_err("cannot stat program: (%s)\n", cmd);
264         exit(117);
265     }
[2591]266@@ -578,10 +715,12 @@ int main(int argc, char *argv[])
[1]267     /*
268      * Error out if the program is writable by others.
269      */
270+#if 0
271     if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) {
272         log_err("file is writable by others: (%s/%s)\n", cwd, cmd);
273         exit(118);
274     }
275+#endif
276 
277     /*
278      * Error out if the file is setuid or setgid.
[2591]279@@ -595,6 +734,7 @@ int main(int argc, char *argv[])
[1]280      * Error out if the target name/group is different from
281      * the name/group of the cwd or the program.
282      */
283+#if 0
284     if ((uid != dir_info.st_uid) ||
285         (gid != dir_info.st_gid) ||
286         (uid != prg_info.st_uid) ||
[2591]287@@ -606,12 +746,14 @@ int main(int argc, char *argv[])
288                 (unsigned long)prg_info.st_uid, (unsigned long)prg_info.st_gid);
[1]289         exit(120);
290     }
291+#endif
292     /*
293      * Error out if the program is not executable for the user.
294      * Otherwise, she won't find any error in the logs except for
[842]295      * "[error] Premature end of script headers: ..."
296      */
297-    if (!(prg_info.st_mode & S_IXUSR)) {
298+    if (!is_static_extension(cmd) && !is_php_extension(cmd) &&
[873]299+        !(prg_info.st_mode & S_IXUSR)) {
[842]300         log_err("file has no execute permission: (%s/%s)\n", cwd, cmd);
[873]301         exit(121);
[842]302     }
[2591]303@@ -660,6 +802,30 @@ int main(int argc, char *argv[])
[1355]304     /*
305      * Execute the command, replacing our image with its own.
306      */
[298]307+    if (is_static_extension(cmd)) {
[1590]308+        if (setenv("PATH_TRANSLATED", cmd, 1) != 0) {
309+            log_err("setenv failed\n");
310+            exit(255);
311+        }
312+        execl(STATIC_CAT_PATH, STATIC_CAT_PATH, (const char *)NULL);
313+        log_err("(%d)%s: static-cat exec failed (%s)\n", errno, strerror(errno), STATIC_CAT_PATH);
[1259]314+        exit(255);
[298]315+    }
[842]316+    if (is_php_extension(cmd)) {
317+        setenv("PHPRC", ".", 1);
318+        argv[1] = PHP_PATH;
319+        argv[2] = "-f";
[2186]320+        /*
321+         * argv[3] is the command to run. argv[4] is either an argument or
322+         * already null. We don't want to pass any arguments through from
323+         * Apache (since they're untrusted), so we chop off the remainder
324+         * of argv here.
325+         */
326+        argv[4] = 0;
[842]327+        execv(PHP_PATH, &argv[1]);
[1354]328+        log_err("(%d)%s: php exec failed (%s)\n", errno, strerror(errno), argv[1]);
[1259]329+        exit(255);
[842]330+    }
[1355]331 #ifdef NEED_HASHBANG_EMUL
332     /* We need the #! emulation when we want to execute scripts */
333     {
[2591]334--
3351.8.1.2
336
Note: See TracBrowser for help on using the repository browser.