Import upstream 0.7.1
[manu/suphp.git] / src / Application.cpp
index bb7ce50..bedba59 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
+    suPHP - (c)2002-2008 Sebastian Marsching <sebastian@marsching.com>
 
     This file is part of suPHP.
 
@@ -33,6 +33,7 @@
 #include "UserInfo.hpp"
 #include "GroupInfo.hpp"
 #include "Util.hpp"
+#include "PathMatcher.hpp"
 
 #include "Application.hpp"
 
@@ -48,7 +49,7 @@ int suPHP::Application::run(CommandLine& cmdline, Environment& env) {
     Configuration config;
     API& api = API_Helper::getSystemAPI();
     Logger& logger = api.getSystemLogger();
-    
+
 #ifdef OPT_CONFIGFILE
     File cfgFile = File(OPT_CONFIGFILE);
 #else
@@ -62,219 +63,271 @@ int suPHP::Application::run(CommandLine& cmdline, Environment& env) {
     // Begin try block - soft exception cannot really be handled before
     // initialization
     try {
-       std::string scriptFilename;
-       
-       // If caller is super-user, print info message and exit
-       if (api.getRealProcessUser().isSuperUser()) {
-           this->printAboutMessage();
-           return 0;
-       }
-       config.readFromFile(cfgFile);
-       
-       // Check permissions (real uid, effective uid)
-       this->checkProcessPermissions(config);
-       
-       // Initialize logger
-       // not done before, because we need super-user privileges for
-       // logging anyway
-       logger.init(config);
-
-       try {
-           scriptFilename = env.getVar("SCRIPT_FILENAME");
-       } catch (KeyNotFoundException& e) {
-           logger.logError("Environment variable SCRIPT_FILENAME not set");
-           this->printAboutMessage();
-           return 1;
-       }
-
-       this->checkScriptFile(scriptFilename, config, env);
-
-       // Root privileges are needed for chroot()
-       // so do this before changing process permissions
-       if (config.getChrootPath().length() > 0) {
-           api.chroot(config.getChrootPath());
-       }
-
-       this->changeProcessPermissions(scriptFilename, config, env);
-
-       interpreter = this->getInterpreter(env, config);
-       targetMode = this->getTargetMode(interpreter);
-       
-       // Prepare environment for new process
-       newEnv = this->prepareEnvironment(env, config, targetMode);
-       
-       // Set PATH_TRANSLATED to SCRIPT_FILENAME, otherwise
-       // the PHP interpreter will not be able to find the script
-       if (targetMode == TARGETMODE_PHP && newEnv.hasVar("PATH_TRANSLATED")) {
-           newEnv.setVar("PATH_TRANSLATED", scriptFilename);
-       }
-       
-       // Log attempt to execute script
-       logger.logInfo("Executing \"" + scriptFilename + "\" as UID "
-                      + Util::intToStr(api.getEffectiveProcessUser().getUid())
-                      + ", GID " 
-                      + Util::intToStr(
-                          api.getEffectiveProcessGroup().getGid()));
-
-       this->executeScript(scriptFilename, interpreter, targetMode, newEnv, 
-                           config);
-       
-       // Function should never return
-       // So, if we get here, return with error code
-       return 1;
+        std::string scriptFilename;
+        UserInfo targetUser;
+        GroupInfo targetGroup;
+
+        // If caller is super-user, print info message and exit
+        if (api.getRealProcessUser().isSuperUser()) {
+            this->printAboutMessage();
+            return 0;
+        }
+        config.readFromFile(cfgFile);
+
+        // Check permissions (real uid, effective uid)
+        this->checkProcessPermissions(config);
+
+        // Initialize logger
+        // not done before, because we need super-user privileges for
+        // logging anyway
+        logger.init(config);
+
+        try {
+            scriptFilename = env.getVar("SCRIPT_FILENAME");
+        } catch (KeyNotFoundException& e) {
+            logger.logError("Environment variable SCRIPT_FILENAME not set");
+            this->printAboutMessage();
+            return 1;
+        }
+
+
+        // Do checks that do not need target user info
+        this->checkScriptFileStage1(scriptFilename, config, env);
+
+        // Find out target user
+        this->checkProcessPermissions(scriptFilename, config, env, targetUser, targetGroup);
+
+        // Now do checks that might require user info
+        this->checkScriptFileStage2(scriptFilename, config, env, targetUser, targetGroup);
+
+        // Root privileges are needed for chroot()
+        // so do this before changing process permissions
+        if (config.getChrootPath().length() > 0) {
+            PathMatcher pathMatcher = PathMatcher(targetUser, targetGroup);
+            std::string chrootPath = pathMatcher.resolveVariables(config.getChrootPath());
+            api.chroot(chrootPath);
+        }
+
+        this->changeProcessPermissions(config, targetUser, targetGroup);
+
+        interpreter = this->getInterpreter(env, config);
+        targetMode = this->getTargetMode(interpreter);
+
+        // Prepare environment for new process
+        newEnv = this->prepareEnvironment(env, config, targetMode);
+
+        // Set PATH_TRANSLATED to SCRIPT_FILENAME, otherwise
+        // the PHP interpreter will not be able to find the script
+        if (targetMode == TARGETMODE_PHP && newEnv.hasVar("PATH_TRANSLATED")) {
+            newEnv.setVar("PATH_TRANSLATED", scriptFilename);
+        }
+
+        // Log attempt to execute script
+        logger.logInfo("Executing \"" + scriptFilename + "\" as UID "
+                       + Util::intToStr(api.getEffectiveProcessUser().getUid())
+                       + ", GID "
+                       + Util::intToStr(
+                           api.getEffectiveProcessGroup().getGid()));
+
+        this->executeScript(scriptFilename, interpreter, targetMode, newEnv,
+                            config);
+
+        // Function should never return
+        // So, if we get here, return with error code
+        return 1;
     } catch (SoftException& e) {
-       if (!config.getErrorsToBrowser()) {
-           std::cerr << e;
-           return 2;
-       }
-       std::cout << "Content-Type: text/html\n"
-                 << "Status: 500\n"
-                 << "\n"
-                 << "<html>\n"
-                 << " <head>\n"
-                 << "  <title>500 Internal Server Error</title>\n"
-                 << " </head>\n"
-                 << " <body>\n"
-                 << "  <h1>Internal Server Error</h1>\n"
-                 << "  <p>" << e.getMessage() << "</p>\n"
-                 << "  <hr/>"
-                 << "  <address>suPHP " << PACKAGE_VERSION << "</address>\n"
-                 << " </body>\n"
-                 << "</html>\n";
+        if (!config.getErrorsToBrowser()) {
+            std::cerr << e;
+            return 2;
+        }
+        std::cout << "Content-Type: text/html\n"
+                  << "Status: 500\n"
+                  << "\n"
+                  << "<html>\n"
+                  << " <head>\n"
+                  << "  <title>500 Internal Server Error</title>\n"
+                  << " </head>\n"
+                  << " <body>\n"
+                  << "  <h1>Internal Server Error</h1>\n"
+                  << "  <p>" << e.getMessage() << "</p>\n"
+                  << "  <hr/>"
+                  << "  <address>suPHP " << PACKAGE_VERSION << "</address>\n"
+                  << " </body>\n"
+                  << "</html>\n";
     }
 }
 
 
 void suPHP::Application::printAboutMessage() {
     std::cerr << "suPHP version " << PACKAGE_VERSION << "\n";
-    std::cerr << "(c) 2002-2005 Sebastian Marsching\n";
+    std::cerr << "(c) 2002-2007 Sebastian Marsching\n";
     std::cerr << std::endl;
     std::cerr << "suPHP has to be called by mod_suphp to work." << std::endl;
 }
 
 
-void suPHP::Application::checkProcessPermissions(Configuration& config) 
+void suPHP::Application::checkProcessPermissions(Configuration& config)
     throw (SecurityException, LookupException) {
     API& api = API_Helper::getSystemAPI();
     if (api.getRealProcessUser() !=
-       api.getUserInfo(config.getWebserverUser())) {
-       throw SecurityException("Calling user is not webserver user!",
-                               __FILE__, __LINE__);
+        api.getUserInfo(config.getWebserverUser())) {
+        throw SecurityException("Calling user is not webserver user!",
+                                __FILE__, __LINE__);
     }
-    
+
     if (!api.getEffectiveProcessUser().isSuperUser()) {
-       throw SecurityException(
-           "Do not have root privileges. Executable not set-uid root?",
-           __FILE__, __LINE__);
+        throw SecurityException(
+            "Do not have root privileges. Executable not set-uid root?",
+            __FILE__, __LINE__);
     }
 }
 
 
-void suPHP::Application::checkScriptFile(
-    const std::string& scriptFilename, 
+void suPHP::Application::checkScriptFileStage1(
+    const std::string& scriptFilename,
     const Configuration& config,
     const Environment& environment) const
     throw (SystemException, SoftException) {
     Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
     File scriptFile = File(scriptFilename);
+    File realScriptFile = File(scriptFile.getRealPath());
 
     // Check wheter file exists
     if (!scriptFile.exists()) {
-       std::string error = "File " + scriptFile.getPath() + " does not exist";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        std::string error = "File " + scriptFile.getPath() + " does not exist";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
-    
-    // Get full path to script file
-
-    File realScriptFile = File(scriptFile.getRealPath());
-    File directory = realScriptFile.getParentDirectory();
-    
-    // Check wheter script is in docroot
-    if (realScriptFile.getPath().find(config.getDocroot()) != 0) {
-       std::string error = "Script \"" + scriptFile.getPath() 
-           + "\" resolving to \"" + realScriptFile.getPath() 
-           + "\" not within configured docroot";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+    if (!realScriptFile.exists()) {
+        std::string error = "File " + realScriptFile.getPath()
+            + " referenced by symlink " +scriptFile.getPath()
+            + " does not exist";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 
     // If enabled, check whether script is in the vhost's docroot
     if (!environment.hasVar("DOCUMENT_ROOT"))
-       throw SoftException("Environment variable DOCUMENT_ROOT not set",
-                               __FILE__, __LINE__);
+        throw SoftException("Environment variable DOCUMENT_ROOT not set",
+                                __FILE__, __LINE__);
     if (config.getCheckVHostDocroot()
-       && realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT")) 
-       != 0) {
-       
-       std::string error = "File \"" + realScriptFile.getPath()
-           + "\" is not in document root of Vhost \""
-           + environment.getVar("DOCUMENT_ROOT") + "\"";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        && realScriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT"))
+        != 0) {
+
+        std::string error = "File \"" + realScriptFile.getPath()
+            + "\" is not in document root of Vhost \""
+            + environment.getVar("DOCUMENT_ROOT") + "\"";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
+    }
+    if (config.getCheckVHostDocroot()
+        && scriptFile.getPath().find(environment.getVar("DOCUMENT_ROOT"))
+        != 0) {
+
+        std::string error = "File \"" + scriptFile.getPath()
+            + "\" is not in document root of Vhost \""
+            + environment.getVar("DOCUMENT_ROOT") + "\"";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 
-    // Check script and directory permissions
+    // Check script permissions
+    // Directories will be checked later
     if (!realScriptFile.hasUserReadBit()) {
-       std::string error = "File \"" + realScriptFile.getPath()
-           + "\" not readable";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
-       
+        std::string error = "File \"" + realScriptFile.getPath()
+            + "\" not readable";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
+
     }
 
     if (!config.getAllowFileGroupWriteable()
-       && realScriptFile.hasGroupWriteBit()) {
-       std::string error = "File \"" + realScriptFile.getPath()
-           + "\" is writeable by group";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
-    }
-    
-    if (!config.getAllowDirectoryGroupWriteable() 
-       && directory.hasGroupWriteBit()) {
-       std::string error = "Directory \"" + directory.getPath()
-           + "\" is writeable by group";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        && realScriptFile.hasGroupWriteBit()) {
+        std::string error = "File \"" + realScriptFile.getPath()
+            + "\" is writeable by group";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 
     if (!config.getAllowFileOthersWriteable()
-       && realScriptFile.hasOthersWriteBit()) {
-       std::string error = "File \"" + realScriptFile.getPath()
-           + "\" is writeable by others";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
-    }
-    
-    if (!config.getAllowDirectoryOthersWriteable()
-       && directory.hasOthersWriteBit()) {
-       std::string error = "Directory \"" + directory.getPath()
-           + "\" is writeable by others";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        && realScriptFile.hasOthersWriteBit()) {
+        std::string error = "File \"" + realScriptFile.getPath()
+            + "\" is writeable by others";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 
     // Check UID/GID of symlink is matching target
     if (scriptFile.getUser() != realScriptFile.getUser()
-       || scriptFile.getGroup() != realScriptFile.getGroup()) {
-       std::string error = "UID or GID of symlink \"" + scriptFile.getPath() 
-           + "\" is not matching its target";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        || scriptFile.getGroup() != realScriptFile.getGroup()) {
+        std::string error = "UID or GID of symlink \"" + scriptFile.getPath()
+            + "\" is not matching its target";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 }
 
+void suPHP::Application::checkScriptFileStage2(
+    const std::string& scriptFilename,
+    const Configuration& config,
+    const Environment& environment,
+    const UserInfo& targetUser,
+    const GroupInfo& targetGroup) const
+    throw (SystemException, SoftException) {
+    Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
+    File scriptFile = File(scriptFilename);
+    PathMatcher pathMatcher = PathMatcher(targetUser, targetGroup);
+
+    // Get full path to script file
+    File realScriptFile = File(scriptFile.getRealPath());
+
+    // Check wheter script is in one of the defined docroots
+    bool file_in_docroot = false;
+    const std::vector<std::string> docroots = config.getDocroots();
+    for (std::vector<std::string>::const_iterator i = docroots.begin(); i != docroots.end(); i++) {
+        std::string docroot = *i;
+        if (pathMatcher.matches(docroot, realScriptFile.getPath())) {
+            file_in_docroot = true;
+            break;
+        }
+    }
+    if (!file_in_docroot) {
+        std::string error = "Script \"" + scriptFile.getPath()
+            + "\" resolving to \"" + realScriptFile.getPath()
+            + "\" not within configured docroot";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
+    }
+    file_in_docroot = false;
+    for (std::vector<std::string>::const_iterator i = docroots.begin(); i != docroots.end(); i++) {
+        std::string docroot = *i;
+        if (pathMatcher.matches(docroot, scriptFile.getPath())) {
+            file_in_docroot = true;
+            break;
+        }
+    }
+    if (!file_in_docroot) {
+        std::string error = "Script \"" + scriptFile.getPath()
+            + "\" not within configured docroot";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
+    }
 
-void suPHP::Application::changeProcessPermissions(
-    const std::string& scriptFilename, 
+    // Check directory ownership and permissions
+    checkParentDirectories(realScriptFile, targetUser, config);
+    checkParentDirectories(scriptFile, targetUser, config);
+}
+
+void suPHP::Application::checkProcessPermissions(
+    const std::string& scriptFilename,
     const Configuration& config,
-    const Environment& environment) const
+    const Environment& environment,
+    UserInfo& targetUser,
+    GroupInfo& targetGroup) const
     throw (SystemException, SoftException, SecurityException) {
-    UserInfo targetUser;
-    GroupInfo targetGroup;
 
-    File scriptFile = File(File(scriptFilename).getRealPath());
+    File scriptFile = File(scriptFilename);
+    File realScriptFile = File(scriptFile.getRealPath());
     API& api = API_Helper::getSystemAPI();
     Logger& logger = api.getSystemLogger();
 
@@ -291,44 +344,44 @@ void suPHP::Application::changeProcessPermissions(
 
     // Check UID/GID of script
     if (scriptFile.getUser().getUid() < config.getMinUid()) {
-       std::string error = "UID of script \"" + scriptFilename
-           + "\" is smaller than min_uid";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        std::string error = "UID of script \"" + scriptFilename
+            + "\" is smaller than min_uid";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
     if (scriptFile.getGroup().getGid() < config.getMinGid()) {
-       std::string error = "GID of script \"" + scriptFilename
-           + "\" is smaller than min_gid";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        std::string error = "GID of script \"" + scriptFilename
+            + "\" is smaller than min_gid";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
-    
+
     // Paranoid and force mode
 
 #if (defined(OPT_USERGROUP_PARANOID) || defined(OPT_USERGROUP_FORCE))
     std::string targetUsername, targetGroupname;
     try {
-       targetUsername = environment.getVar("SUPHP_USER");
-       targetGroupname = environment.getVar("SUPHP_GROUP");
+        targetUsername = environment.getVar("SUPHP_USER");
+        targetGroupname = environment.getVar("SUPHP_GROUP");
     } catch (KeyNotFoundException& e) {
-       throw SecurityException(
-           "Environment variable SUPHP_USER or SUPHP_GROUP not set", 
-           __FILE__, __LINE__);
+        throw SecurityException(
+            "Environment variable SUPHP_USER or SUPHP_GROUP not set",
+            __FILE__, __LINE__);
     }
-    
+
     if (targetUsername[0] == '#' && targetUsername.find_first_not_of(
-           "0123456789", 1) == std::string::npos) {
-       targetUser = api.getUserInfo(Util::strToInt(targetUsername.substr(1)));
+            "0123456789", 1) == std::string::npos) {
+        targetUser = api.getUserInfo(Util::strToInt(targetUsername.substr(1)));
     } else {
-       targetUser = api.getUserInfo(targetUsername);
+        targetUser = api.getUserInfo(targetUsername);
     }
 
     if (targetGroupname[0] == '#' && targetGroupname.find_first_not_of(
-           "0123456789", 1) == std::string::npos) {
-       targetGroup = api.getGroupInfo(
-           Util::strToInt(targetGroupname.substr(1)));
+            "0123456789", 1) == std::string::npos) {
+        targetGroup = api.getGroupInfo(
+            Util::strToInt(targetGroupname.substr(1)));
     } else {
-       targetGroup = api.getGroupInfo(targetGroupname);
+        targetGroup = api.getGroupInfo(targetGroupname);
     }
 #endif // OPT_USERGROUP_PARANOID || OPT_USERGROUP_FORCE
 
@@ -338,35 +391,41 @@ void suPHP::Application::changeProcessPermissions(
     targetUser = scriptFile.getUser();
     targetGroup = scriptFile.getGroup();
 #endif // OPT_USERGROUP_OWNER
-    
+
     // Paranoid mode only
 
 #ifdef OPT_USERGROUP_PARANOID
     if (targetUser != scriptFile.getUser()) {
-       std::string error ="Mismatch between target UID ("
-           + Util::intToStr(targetUser.getUid()) + ") and UID (" 
-           + Util::intToStr(scriptFile.getUser().getUid()) + ") of file \"" 
-           + scriptFile.getPath() + "\"";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        std::string error ="Mismatch between target UID ("
+            + Util::intToStr(targetUser.getUid()) + ") and UID ("
+            + Util::intToStr(scriptFile.getUser().getUid()) + ") of file \""
+            + scriptFile.getPath() + "\"";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
 
     if (targetGroup != scriptFile.getGroup()) {
-       std::string error ="Mismatch between target GID ("
-           + Util::intToStr(targetGroup.getGid()) + ") and GID (" 
-           + Util::intToStr(scriptFile.getGroup().getGid()) + ") of file \"" 
-           + scriptFile.getPath() + "\"";
-       logger.logWarning(error);
-       throw SoftException(error, __FILE__, __LINE__);
+        std::string error ="Mismatch between target GID ("
+            + Util::intToStr(targetGroup.getGid()) + ") and GID ("
+            + Util::intToStr(scriptFile.getGroup().getGid()) + ") of file \""
+            + scriptFile.getPath() + "\"";
+        logger.logWarning(error);
+        throw SoftException(error, __FILE__, __LINE__);
     }
-#endif // OPT_USERGROUP_PARANOID    
+#endif // OPT_USERGROUP_PARANOID
+}
 
-    // Common code used for all modes
+void suPHP::Application::changeProcessPermissions(
+    const Configuration& config,
+    const UserInfo& targetUser,
+    const GroupInfo& targetGroup) const
+    throw (SystemException, SoftException, SecurityException) {
+    API& api = API_Helper::getSystemAPI();
 
     // Set new group first, because we still need super-user privileges
     // for this
     api.setProcessGroup(targetGroup);
-    
+
     // Then set new user
     api.setProcessUser(targetUser);
 
@@ -379,44 +438,44 @@ Environment suPHP::Application::prepareEnvironment(
     throw (KeyNotFoundException) {
     // Create environment for new process from old environment
     Environment env = sourceEnv;
-    
+
     // Delete unwanted environment variables
     if (env.hasVar("LD_PRELOAD"))
-       env.deleteVar("LD_PRELOAD");
+        env.deleteVar("LD_PRELOAD");
     if (env.hasVar("LD_LIBRARY_PATH"))
-       env.deleteVar("LD_LIBRARY_PATH");
+        env.deleteVar("LD_LIBRARY_PATH");
     if (env.hasVar("SUPHP_USER"))
-       env.deleteVar("SUPHP_USER");
+        env.deleteVar("SUPHP_USER");
     if (env.hasVar("SUPHP_GROUP"))
-       env.deleteVar("SUPHP_GROUP");
+        env.deleteVar("SUPHP_GROUP");
     if (env.hasVar("SUPHP_HANDLER"))
-       env.deleteVar("SUPHP_HANDLER");
+        env.deleteVar("SUPHP_HANDLER");
     if (env.hasVar("SUPHP_AUTH_USER"))
-       env.deleteVar("SUPHP_AUTH_USER");
+        env.deleteVar("SUPHP_AUTH_USER");
     if (env.hasVar("SUPHP_AUTH_PW"))
-       env.deleteVar("SUPHP_AUTH_PW");
+        env.deleteVar("SUPHP_AUTH_PW");
     if (env.hasVar("SUPHP_PHP_CONFIG"))
-       env.deleteVar("SUPHP_PHP_CONFIG");
-    
+        env.deleteVar("SUPHP_PHP_CONFIG");
+
     // Reset PATH
     env.putVar("PATH", config.getEnvPath());
 
     // If we are in PHP mode, set PHP specific variables
     if (mode == TARGETMODE_PHP) {
-       if (sourceEnv.hasVar("SUPHP_PHP_CONFIG"))
-           env.putVar("PHPRC", sourceEnv.getVar("SUPHP_PHP_CONFIG"));
-       if (sourceEnv.hasVar("SUPHP_AUTH_USER")
-           && sourceEnv.hasVar("SUPHP_AUTH_PW")) {
-           env.putVar("PHP_AUTH_USER", sourceEnv.getVar("SUPHP_AUTH_USER"));
-           env.putVar("PHP_AUTH_PW", sourceEnv.getVar("SUPHP_AUTH_PW"));
-       }
-
-       // PHP may need this, when compiled with security features
-       if (!env.hasVar("REDIRECT_STATUS")) {
-           env.putVar("REDIRECT_STATUS", "200");
-       }
+        if (sourceEnv.hasVar("SUPHP_PHP_CONFIG"))
+            env.putVar("PHPRC", sourceEnv.getVar("SUPHP_PHP_CONFIG"));
+        if (sourceEnv.hasVar("SUPHP_AUTH_USER")
+            && sourceEnv.hasVar("SUPHP_AUTH_PW")) {
+            env.putVar("PHP_AUTH_USER", sourceEnv.getVar("SUPHP_AUTH_USER"));
+            env.putVar("PHP_AUTH_PW", sourceEnv.getVar("SUPHP_AUTH_PW"));
+        }
+
+        // PHP may need this, when compiled with security features
+        if (!env.hasVar("REDIRECT_STATUS")) {
+            env.putVar("REDIRECT_STATUS", "200");
+        }
     }
-    
+
     return env;
 }
 
@@ -425,18 +484,18 @@ std::string suPHP::Application::getInterpreter(
     const Environment& env, const Configuration& config)
     throw (SecurityException) {
     if (!env.hasVar("SUPHP_HANDLER"))
-       throw SecurityException("Environment variable SUPHP_HANDLER not set",
-                               __FILE__, __LINE__);
+        throw SecurityException("Environment variable SUPHP_HANDLER not set",
+                                __FILE__, __LINE__);
     std::string handler = env.getVar("SUPHP_HANDLER");
-    
+
     std::string interpreter = "";
     try {
-       interpreter = config.getInterpreter(handler);
+        interpreter = config.getInterpreter(handler);
     } catch (KeyNotFoundException& e) {
-       throw SecurityException ("Handler not found in configuration", e,
-                                __FILE__, __LINE__);
+        throw SecurityException ("Handler not found in configuration", e,
+                                 __FILE__, __LINE__);
     }
-    
+
     return interpreter;
 }
 
@@ -444,55 +503,92 @@ std::string suPHP::Application::getInterpreter(
 TargetMode suPHP::Application::getTargetMode(const std::string& interpreter)
     throw (SecurityException) {
     if (interpreter.substr(0, 4) == "php:")
-       return TARGETMODE_PHP;
+        return TARGETMODE_PHP;
     else if (interpreter == "execute:!self")
-       return TARGETMODE_SELFEXECUTE;
+        return TARGETMODE_SELFEXECUTE;
     else
-       throw SecurityException("Unknown Interpreter: " + interpreter,
-                               __FILE__, __LINE__);
+        throw SecurityException("Unknown Interpreter: " + interpreter,
+                                __FILE__, __LINE__);
 }
 
 
 void suPHP::Application::executeScript(const std::string& scriptFilename,
-                                      const std::string& interpreter,
-                                      TargetMode mode,
-                                      const Environment& env,
-                                      const Configuration& config) const
+                                       const std::string& interpreter,
+                                       TargetMode mode,
+                                       const Environment& env,
+                                       const Configuration& config) const
     throw (SoftException) {
     try {
-       // Change working directory to script path
-       API_Helper::getSystemAPI().setCwd(
-           File(scriptFilename).getParentDirectory().getPath());
-       if (mode == TARGETMODE_PHP) {
-           std::string interpreterPath = interpreter.substr(4);
-           CommandLine cline;
-           cline.putArgument(interpreterPath);
-           API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
-       } else if (mode == TARGETMODE_SELFEXECUTE) {
-           CommandLine cline;
-           cline.putArgument(scriptFilename);
-           API_Helper::getSystemAPI().execute(scriptFilename, cline, env);
-       }
+        // Change working directory to script path
+        API_Helper::getSystemAPI().setCwd(
+            File(scriptFilename).getParentDirectory().getPath());
+        if (mode == TARGETMODE_PHP) {
+            std::string interpreterPath = interpreter.substr(4);
+            CommandLine cline;
+            cline.putArgument(interpreterPath);
+            API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
+        } else if (mode == TARGETMODE_SELFEXECUTE) {
+            CommandLine cline;
+            cline.putArgument(scriptFilename);
+            API_Helper::getSystemAPI().execute(scriptFilename, cline, env);
+        }
     } catch (SystemException& e) {
-       throw SoftException("Could not execute script \"" + scriptFilename
-                               + "\"", e, __FILE__, __LINE__);
+        throw SoftException("Could not execute script \"" + scriptFilename
+                                + "\"", e, __FILE__, __LINE__);
     }
 }
 
 
+void suPHP::Application::checkParentDirectories(const File& file,
+                                               const UserInfo& owner,
+                                               const Configuration& config) const throw (SoftException) {
+    File directory = file;
+    Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
+    do {
+        directory = directory.getParentDirectory();
+
+        UserInfo directoryOwner = directory.getUser();
+        if (directoryOwner != owner && !directoryOwner.isSuperUser()) {
+            std::string error = "Directory " + directory.getPath()
+                + " is not owned by " + owner.getUsername();
+            logger.logWarning(error);
+            throw SoftException(error, __FILE__, __LINE__);
+        }
+
+        if (!directory.isSymlink()
+            && !config.getAllowDirectoryGroupWriteable()
+            && directory.hasGroupWriteBit()) {
+            std::string error = "Directory \"" + directory.getPath()
+                + "\" is writeable by group";
+            logger.logWarning(error);
+            throw SoftException(error, __FILE__, __LINE__);
+        }
+
+        if (!directory.isSymlink()
+            && !config.getAllowDirectoryOthersWriteable()
+            && directory.hasOthersWriteBit()) {
+            std::string error = "Directory \"" + directory.getPath()
+                + "\" is writeable by others";
+            logger.logWarning(error);
+            throw SoftException(error, __FILE__, __LINE__);
+        }
+    } while (directory.getPath() != "/");
+}
+
+
 int main(int argc, char **argv) {
     try {
-       API& api = API_Helper::getSystemAPI();
-       CommandLine cmdline;
-       Environment env;
-       Application app;
-       for (int i=0; i<argc; i++) {
-           cmdline.putArgument(argv[i]);
-       }
-       env = api.getProcessEnvironment();
-       return app.run(cmdline, env);
+        API& api = API_Helper::getSystemAPI();
+        CommandLine cmdline;
+        Environment env;
+        Application app;
+        for (int i=0; i<argc; i++) {
+            cmdline.putArgument(argv[i]);
+        }
+        env = api.getProcessEnvironment();
+        return app.run(cmdline, env);
     } catch (Exception& e) {
-       std::cerr << e;
-       return 1;
+        std::cerr << e;
+        return 1;
     }
 }