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
  • configure.in

    From 427d432a56df94d69a11cc438b08adb070615005 Mon Sep 17 00:00:00 2001
    From: Alexander Chernyakhovsky <achernya@mit.edu>
    Date: Fri, 3 May 2013 21:38:58 -0400
    Subject: [PATCH] Add scripts-specific support to suexec
    
    This patch make suexec aware of static-cat, Scripts' tool to serve
    static content out of AFS.  Specifically, this introduces a whitelist
    of extensions for which suexec is supposed to invoke static-cat as a
    content-handler.
    
    Additionally, this patch also sets JAVA_TOOL_OPTIONS, to allow the JVM
    to start up in Scripts' limited memory environment.
    
    Furthermore, this patch deals with some of suexec's paranoia being
    incorrect in an AFS world, by ignoring some of the irrelevant stat
    results.
    
    Finally, add support for invoking php-cgi for php files, in a safe
    manner that will strip arguments passed by Apache to php-cgi.
    ---
     configure.in     |   4 ++
     support/suexec.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
     2 files changed, 173 insertions(+), 3 deletions(-)
    
    diff --git a/configure.in b/configure.in
    index 811aace..a95349f 100644
    a b AC_ARG_WITH(suexec-userdir, 
    721721APACHE_HELP_STRING(--with-suexec-userdir,User subdirectory),[
    722722  AC_DEFINE_UNQUOTED(AP_USERDIR_SUFFIX, "$withval", [User subdirectory] ) ] )
    723723
     724AC_ARG_WITH(suexec-trusteddir,
     725APACHE_HELP_STRING(--with-suexec-trusteddir,Trusted SuExec directory),[
     726  AC_DEFINE_UNQUOTED(AP_TRUSTED_DIRECTORY, "$withval", [Trusted SuExec directory] ) ] )
     727
    724728AC_ARG_WITH(suexec-docroot,
    725729APACHE_HELP_STRING(--with-suexec-docroot,SuExec root directory),[
    726730  AC_DEFINE_UNQUOTED(AP_DOC_ROOT, "$withval", [SuExec root directory] ) ] )
  • support/suexec.c

    diff --git a/support/suexec.c b/support/suexec.c
    index 32e7320..3a4d802 100644
    a b  
    3030 *
    3131 */
    3232
     33#define STATIC_CAT_PATH "/usr/bin/static-cat"
     34#define PHP_PATH "/usr/bin/php-cgi"
     35
    3336#include "apr.h"
    3437#include "ap_config.h"
    3538#include "suexec.h"
    static void clean_env(void) 
    268272    environ = cleanenv;
    269273}
    270274
     275static const char *static_extensions[] = {
     276    "html",
     277    "css",
     278    "gif",
     279    "jpg",
     280    "png",
     281    "htm",
     282    "jpeg",
     283    "js",
     284    "ico",
     285    "xml",
     286    "xsl",
     287    "tiff",
     288    "tif",
     289    "tgz",
     290    "tar",
     291    "jar",
     292    "zip",
     293    "pdf",
     294    "ps",
     295    "doc",
     296    "xls",
     297    "ppt",
     298    "dot",
     299    "docx",
     300    "dotx",
     301    "docm",
     302    "dotm",
     303    "xlt",
     304    "xla",
     305    "xlsx",
     306    "xltx",
     307    "xlsm",
     308    "xltm",
     309    "xlam",
     310    "xlsb",
     311    "pot",
     312    "pps",
     313    "ppa",
     314    "pptx",
     315    "potx",
     316    "ppsx",
     317    "ppam",
     318    "pptm",
     319    "potm",
     320    "ppsm",
     321    "swf",
     322    "mp3",
     323    "mov",
     324    "wmv",
     325    "mpg",
     326    "mpeg",
     327    "avi",
     328    "il",
     329    "xhtml",
     330    "svg",
     331    "xaml",
     332    "xap",
     333    "wav",
     334    "mid",
     335    "midi",
     336    "ttf",
     337    "otf",
     338    "odc",
     339    "odb",
     340    "odf",
     341    "odg",
     342    "otg",
     343    "odi",
     344    "odp",
     345    "otp",
     346    "ods",
     347    "ots",
     348    "odt",
     349    "odm",
     350    "ott",
     351    "oth",
     352    "eot",
     353    "woff",
     354    "woff2",
     355    NULL
     356};
     357
     358static int is_static_extension(const char *file)
     359{
     360    const char *extension = strrchr(file, '.');
     361    const char **p;
     362    if (extension == NULL) return 0;
     363    for (p = static_extensions; *p; ++p) {
     364        if (strcasecmp(extension + 1, *p) == 0) return 1;
     365    }
     366    return 0;
     367}
     368
     369static int is_php_extension(const char *file)
     370{
     371    const char *extension = strrchr(file, '.');
     372    if (extension == NULL) return 0;
     373    return strcmp(extension + 1, "php") == 0;
     374}
     375
    271376int main(int argc, char *argv[])
    272377{
    273378    int userdir = 0;        /* ~userdir flag             */
     379    int trusteddir = 0;     /* TRUSTED_DIRECTORY flag    */
    274380    uid_t uid;              /* user information          */
    275381    gid_t gid;              /* target group placeholder  */
    276382    char *target_uname;     /* target user name          */
    int main(int argc, char *argv[]) 
    290393     * Start with a "clean" environment
    291394     */
    292395    clean_env();
     396    setenv("JAVA_TOOL_OPTIONS", "-Xmx128M", 1); /* scripts.mit.edu local hack */
    293397
    294398    /*
    295399     * Check existence/validity of the UID of the user
    int main(int argc, char *argv[]) 
    373477#endif /*_OSD_POSIX*/
    374478
    375479    /*
     480     * First check if this is an absolute path to the directory
     481     * of trusted executables. These are supposed to be security
     482     * audited to check parameters and validity on their own...
     483     */
     484    if (strstr(cmd, AP_TRUSTED_DIRECTORY) == cmd) {
     485        if (strstr(cmd, "/../") != NULL) {
     486            log_err("invalid command (%s)\n", cmd);
     487            exit(104);
     488        }
     489        trusteddir = 1;
     490        goto TRUSTED_DIRECTORY;
     491    }
     492
     493    /*
    376494     * Check for a leading '/' (absolute path) in the command to be executed,
    377495     * or attempts to back up out of the current directory,
    378496     * to protect against attacks.  If any are
    int main(int argc, char *argv[]) 
    394512        userdir = 1;
    395513    }
    396514
     515TRUSTED_DIRECTORY:
    397516    /*
    398517     * Error out if the target username is invalid.
    399518     */
    int main(int argc, char *argv[]) 
    482601     * Error out if attempt is made to execute as root or as
    483602     * a UID less than AP_UID_MIN.  Tsk tsk.
    484603     */
    485     if ((uid == 0) || (uid < AP_UID_MIN)) {
     604    if ((uid == 0) || (uid < AP_UID_MIN && uid != 102)) { /* uid 102 = signup  */
    486605        log_err("cannot run as forbidden uid (%lu/%s)\n", (unsigned long)uid, cmd);
    487606        exit(107);
    488607    }
    int main(int argc, char *argv[]) 
    514633        log_err("failed to setuid (%lu: %s)\n", (unsigned long)uid, cmd);
    515634        exit(110);
    516635    }
     636    setenv("HOME", target_homedir, 1);
    517637
    518638    /*
    519639     * Get the current working directory, as well as the proper
    int main(int argc, char *argv[]) 
    536656            log_err("cannot get docroot information (%s)\n", target_homedir);
    537657            exit(112);
    538658        }
     659        size_t expected_len = strlen(target_homedir)+1+strlen(AP_USERDIR_SUFFIX)+1;
     660        char *expected = malloc(expected_len);
     661        snprintf(expected, expected_len, "%s/%s", target_homedir, AP_USERDIR_SUFFIX);
     662        if (strncmp(cwd, expected, expected_len-1) != 0) {
     663            log_err("error: file's directory not a subdirectory of user's home directory (%s, %s)\n", cwd, expected);
     664            exit(114);
     665        }
     666    }
     667    else if (trusteddir) {
     668        if (((chdir(AP_TRUSTED_DIRECTORY)) != 0) ||
     669            ((getcwd(dwd, AP_MAXPATH)) == NULL) |
     670            ((chdir(cwd)) != 0)) {
     671            log_err("cannot get docroot information (%s)\n", AP_TRUSTED_DIRECTORY);
     672            exit(112);
     673        }
    539674    }
    540675    else {
    541676        if (((chdir(AP_DOC_ROOT)) != 0) ||
    int main(int argc, char *argv[]) 
    562697    /*
    563698     * Error out if cwd is writable by others.
    564699     */
     700#if 0
    565701    if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
    566702        log_err("directory is writable by others: (%s)\n", cwd);
    567703        exit(116);
    568704    }
     705#endif
    569706
    570707    /*
    571708     * Error out if we cannot stat the program.
    572709     */
    573     if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) {
     710    if (((lstat(cmd, &prg_info)) != 0) /*|| (S_ISLNK(prg_info.st_mode))*/) {
    574711        log_err("cannot stat program: (%s)\n", cmd);
    575712        exit(117);
    576713    }
    int main(int argc, char *argv[]) 
    578715    /*
    579716     * Error out if the program is writable by others.
    580717     */
     718#if 0
    581719    if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) {
    582720        log_err("file is writable by others: (%s/%s)\n", cwd, cmd);
    583721        exit(118);
    584722    }
     723#endif
    585724
    586725    /*
    587726     * Error out if the file is setuid or setgid.
    int main(int argc, char *argv[]) 
    595734     * Error out if the target name/group is different from
    596735     * the name/group of the cwd or the program.
    597736     */
     737#if 0
    598738    if ((uid != dir_info.st_uid) ||
    599739        (gid != dir_info.st_gid) ||
    600740        (uid != prg_info.st_uid) ||
    int main(int argc, char *argv[]) 
    606746                (unsigned long)prg_info.st_uid, (unsigned long)prg_info.st_gid);
    607747        exit(120);
    608748    }
     749#endif
    609750    /*
    610751     * Error out if the program is not executable for the user.
    611752     * Otherwise, she won't find any error in the logs except for
    612753     * "[error] Premature end of script headers: ..."
    613754     */
    614     if (!(prg_info.st_mode & S_IXUSR)) {
     755    if (!is_static_extension(cmd) && !is_php_extension(cmd) &&
     756        !(prg_info.st_mode & S_IXUSR)) {
    615757        log_err("file has no execute permission: (%s/%s)\n", cwd, cmd);
    616758        exit(121);
    617759    }
    int main(int argc, char *argv[]) 
    660802    /*
    661803     * Execute the command, replacing our image with its own.
    662804     */
     805    if (is_static_extension(cmd)) {
     806        if (setenv("PATH_TRANSLATED", cmd, 1) != 0) {
     807            log_err("setenv failed\n");
     808            exit(255);
     809        }
     810        execl(STATIC_CAT_PATH, STATIC_CAT_PATH, (const char *)NULL);
     811        log_err("(%d)%s: static-cat exec failed (%s)\n", errno, strerror(errno), STATIC_CAT_PATH);
     812        exit(255);
     813    }
     814    if (is_php_extension(cmd)) {
     815        setenv("PHPRC", ".", 1);
     816        argv[1] = PHP_PATH;
     817        argv[2] = "-f";
     818        /*
     819         * argv[3] is the command to run. argv[4] is either an argument or
     820         * already null. We don't want to pass any arguments through from
     821         * Apache (since they're untrusted), so we chop off the remainder
     822         * of argv here.
     823         */
     824        argv[4] = 0;
     825        execv(PHP_PATH, &argv[1]);
     826        log_err("(%d)%s: php exec failed (%s)\n", errno, strerror(errno), argv[1]);
     827        exit(255);
     828    }
    663829#ifdef NEED_HASHBANG_EMUL
    664830    /* We need the #! emulation when we want to execute scripts */
    665831    {
Note: See TracBrowser for help on using the repository browser.