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

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