Common subdirectories: suphp-0.7.1/src-o/apache and suphp-0.7.1/src/apache
Common subdirectories: suphp-0.7.1/src-o/apache2 and suphp-0.7.1/src/apache2
diff -up suphp-0.7.1/src-o/Application.cpp suphp-0.7.1/src/Application.cpp
--- suphp-0.7.1/src-o/Application.cpp	2009-03-14 14:55:25.000000000 -0300
+++ suphp-0.7.1/src/Application.cpp	2010-06-23 10:39:41.000000000 -0300
@@ -66,6 +66,12 @@ int suPHP::Application::run(CommandLine&
         std::string scriptFilename;
         UserInfo targetUser;
         GroupInfo targetGroup;
+#ifdef COMMON_POOL
+        UserInfo commonUser;
+        GroupInfo commonGroup;
+#endif // COMMON_POOL
+        std::string documentRoot; /* To use in chroot fix */
+        std::string serverRoot; /* To use in chroot fix */
 
         // If caller is super-user, print info message and exit
         if (api.getRealProcessUser().isSuperUser()) {
@@ -73,6 +79,14 @@ int suPHP::Application::run(CommandLine&
             return 0;
         }
         config.readFromFile(cfgFile);
+	
+       try {
+            documentRoot = env.getVar("DOCUMENT_ROOT");
+       } catch (KeyNotFoundException& e) {
+           logger.logError("Environment variable DOCUMENT_ROOT not set");
+           this->printAboutMessage();
+           return 0;
+       }
 
         // Check permissions (real uid, effective uid)
         this->checkProcessPermissions(config);
@@ -95,10 +109,20 @@ int suPHP::Application::run(CommandLine&
         this->checkScriptFileStage1(scriptFilename, config, env);
 
         // Find out target user
-        this->checkProcessPermissions(scriptFilename, config, env, targetUser, targetGroup);
+        this->checkProcessPermissions(scriptFilename, config, env, targetUser, targetGroup
+#ifdef COMMON_POOL
+		, commonUser,
+		commonGroup
+#endif // COMMON_POOL
+	);
 
         // Now do checks that might require user info
-        this->checkScriptFileStage2(scriptFilename, config, env, targetUser, targetGroup);
+        this->checkScriptFileStage2(scriptFilename, config, env, targetUser, targetGroup
+#ifdef COMMON_POOL
+		, commonUser
+//		commonGroup
+#endif // COMMON_POOL
+);
 
         // Root privileges are needed for chroot()
         // so do this before changing process permissions
@@ -106,7 +130,18 @@ int suPHP::Application::run(CommandLine&
             PathMatcher pathMatcher = PathMatcher(targetUser, targetGroup);
             std::string chrootPath = pathMatcher.resolveVariables(config.getChrootPath());
             api.chroot(chrootPath);
+		
+            /* Correct arguments to the chroot value ... */
+
+            documentRoot.erase(0, chrootPath.length());
+            scriptFilename.erase(0, chrootPath.length());
+            serverRoot.erase(0, chrootPath.length());
+
+            env.setVar("DOCUMENT_ROOT", documentRoot);
+            env.setVar("SCRIPT_FILENAME", scriptFilename);
+            env.setVar("SERVER_ROOT", scriptFilename);
         }
+	
 
         this->changeProcessPermissions(config, targetUser, targetGroup);
 
@@ -256,15 +291,6 @@ void suPHP::Application::checkScriptFile
         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__);
-    }
 }
 
 void suPHP::Application::checkScriptFileStage2(
@@ -272,7 +298,12 @@ void suPHP::Application::checkScriptFile
     const Configuration& config,
     const Environment& environment,
     const UserInfo& targetUser,
-    const GroupInfo& targetGroup) const
+    const GroupInfo& targetGroup
+#ifdef COMMON_POOL
+    , const UserInfo& commonUser
+//    const GroupInfo& commonGroup
+#endif // COMMON_POOL
+) const
     throw (SystemException, SoftException) {
     Logger& logger = API_Helper::getSystemAPI().getSystemLogger();
     File scriptFile = File(scriptFilename);
@@ -281,7 +312,24 @@ void suPHP::Application::checkScriptFile
     // Get full path to script file
     File realScriptFile = File(scriptFile.getRealPath());
 
-    // Check wheter script is in one of the defined docroots
+    /* Code borrowed from checkScriptFileStage2, since commonUser may be the owner of the directory */
+    // Check UID/GID of symlink is matching target
+    if (scriptFile.getUser() != realScriptFile.getUser()
+        || scriptFile.getGroup() != realScriptFile.getGroup()) {
+	/* Check if it hasn't the others write bit and [ if the owner isn't root OR commonUser ] and [if it the gowner is root OR it isn't group-writeable ] */
+	if (!( realScriptFile.hasOthersWriteBit() && (realScriptFile.getUser().isSuperUser()
+#ifdef COMMON_POOL
+		|| realScriptFile.getUser() == commonUser
+#endif // COMMON_POOL
+		) && (realScriptFile.hasGroupWriteBit() || realScriptFile.getGroup()==0) )) {
+		std::string error = "UID or GID of symlink \"" + scriptFile.getPath()
+		    + "\" is not matching its target";
+		logger.logWarning(error);
+		throw SoftException(error, __FILE__, __LINE__);
+	}
+    }
+
+    // Check whether 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++) {
@@ -314,8 +362,16 @@ void suPHP::Application::checkScriptFile
     }
 
     // Check directory ownership and permissions
-    checkParentDirectories(realScriptFile, targetUser, config);
-    checkParentDirectories(scriptFile, targetUser, config);
+    checkParentDirectories(realScriptFile, targetUser, config
+#ifdef COMMON_POOL
+    ,commonUser
+#endif // COMMON_POOL    
+    );
+    checkParentDirectories(scriptFile, targetUser, config
+#ifdef COMMON_POOL
+    , commonUser
+#endif // COMMON_POOL
+    );
 }
 
 void suPHP::Application::checkProcessPermissions(
@@ -323,7 +379,12 @@ void suPHP::Application::checkProcessPer
     const Configuration& config,
     const Environment& environment,
     UserInfo& targetUser,
-    GroupInfo& targetGroup) const
+    GroupInfo& targetGroup
+#ifdef COMMON_POOL
+    , UserInfo& commonUser,
+    GroupInfo& commonGroup
+#endif // COMMON_POOL
+) const
     throw (SystemException, SoftException, SecurityException) {
 
     File scriptFile = File(scriptFilename);
@@ -395,7 +456,34 @@ void suPHP::Application::checkProcessPer
     // Paranoid mode only
 
 #ifdef OPT_USERGROUP_PARANOID
-    if (targetUser != scriptFile.getUser()) {
+
+#ifdef COMMON_POOL
+    std::string commonUsername, commonGroupname;
+    
+    commonUsername = config.getCommonUser();
+    commonGroupname = config.getCommonGroup();
+
+    if (commonUsername[0] == '#' && commonUsername.find_first_not_of(
+            "0123456789", 1) == std::string::npos) {
+        commonUser = api.getUserInfo(Util::strToInt(commonUsername.substr(1)));
+    } else {
+        commonUser = api.getUserInfo(commonUsername);
+    }
+
+    if (commonGroupname[0] == '#' && commonGroupname.find_first_not_of(
+            "0123456789", 1) == std::string::npos) {
+        commonGroup = api.getGroupInfo(
+            Util::strToInt(commonGroupname.substr(1)));
+    } else {
+        commonGroup = api.getGroupInfo(commonGroupname);
+    }
+#endif // COMMON_POOL
+    
+    if (targetUser != scriptFile.getUser()
+#ifdef COMMON_POOL
+	&& commonUser != scriptFile.getUser()
+#endif // COMMON_POOL
+    ) {
         std::string error ="Mismatch between target UID ("
             + Util::intToStr(targetUser.getUid()) + ") and UID ("
             + Util::intToStr(scriptFile.getUser().getUid()) + ") of file \""
@@ -404,7 +492,11 @@ void suPHP::Application::checkProcessPer
         throw SoftException(error, __FILE__, __LINE__);
     }
 
-    if (targetGroup != scriptFile.getGroup()) {
+    if (targetGroup != scriptFile.getGroup()
+#ifdef COMMON_POOL
+	&& commonGroup != scriptFile.getGroup()
+#endif // COMMON_POOL
+    ) {
         std::string error ="Mismatch between target GID ("
             + Util::intToStr(targetGroup.getGid()) + ") and GID ("
             + Util::intToStr(scriptFile.getGroup().getGid()) + ") of file \""
@@ -506,6 +598,8 @@ TargetMode suPHP::Application::getTarget
         return TARGETMODE_PHP;
     else if (interpreter == "execute:!self")
         return TARGETMODE_SELFEXECUTE;
+    else if (interpreter.substr(0, 8) == "execute:")
+        return TARGETMODE_OTHER;
     else
         throw SecurityException("Unknown Interpreter: " + interpreter,
                                 __FILE__, __LINE__);
@@ -522,10 +616,13 @@ void suPHP::Application::executeScript(c
         // Change working directory to script path
         API_Helper::getSystemAPI().setCwd(
             File(scriptFilename).getParentDirectory().getPath());
-        if (mode == TARGETMODE_PHP) {
-            std::string interpreterPath = interpreter.substr(4);
+        if (mode == TARGETMODE_PHP || mode == TARGETMODE_OTHER) {
+            std::string interpreterPath = interpreter.substr((mode==TARGETMODE_OTHER)?8:4);
             CommandLine cline;
-            cline.putArgument(interpreterPath);
+            if (mode==TARGETMODE_OTHER) {
+		    cline.putArgument(interpreterPath);
+	    }
+            cline.putArgument(scriptFilename);
             API_Helper::getSystemAPI().execute(interpreterPath, cline, env);
         } else if (mode == TARGETMODE_SELFEXECUTE) {
             CommandLine cline;
@@ -541,14 +638,22 @@ void suPHP::Application::executeScript(c
 
 void suPHP::Application::checkParentDirectories(const File& file,
                                                const UserInfo& owner,
-                                               const Configuration& config) const throw (SoftException) {
+                                               const Configuration& config
+#ifdef COMMON_POOL
+						, const UserInfo& commonUser
+#endif // COMMON_POOL
+) 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()) {
+        if (!config.getAllowDirectoryOwnedByOther() && directoryOwner != owner && !directoryOwner.isSuperUser()
+#ifdef COMMON_POOL
+		&& directoryOwner != commonUser
+#endif // COMMON_POOL
+		) {
             std::string error = "Directory " + directory.getPath()
                 + " is not owned by " + owner.getUsername();
             logger.logWarning(error);
diff -up suphp-0.7.1/src-o/Application.hpp suphp-0.7.1/src/Application.hpp
--- suphp-0.7.1/src-o/Application.hpp	2008-03-29 14:48:59.000000000 -0300
+++ suphp-0.7.1/src/Application.hpp	2010-06-23 10:40:50.000000000 -0300
@@ -26,7 +26,8 @@ namespace suPHP {
 
 enum TargetMode {
     TARGETMODE_PHP,
-    TARGETMODE_SELFEXECUTE
+    TARGETMODE_SELFEXECUTE,
+    TARGETMODE_OTHER
 };
 
 #define SUPHP_APPLICATION_H
@@ -78,7 +79,11 @@ namespace suPHP {
                              const Configuration& config, 
                              const Environment& environment,
                              const UserInfo& targetUser,
-                             const GroupInfo& targetGroup) const
+                             const GroupInfo& targetGroup
+#ifdef COMMON_POOL
+                             , const UserInfo& commonUser
+#endif
+			     ) const
             throw (SystemException, SoftException);
         
         /**
@@ -89,7 +94,12 @@ namespace suPHP {
                                       const Configuration& config,
                                       const Environment& environment,
                                       UserInfo& targetUser,
-                                      GroupInfo& targetGroup) const
+                                      GroupInfo& targetGroup
+#ifdef COMMON_POOL
+                                      , UserInfo& commonUser,
+                                      GroupInfo& commonGroup
+#endif
+				      ) const
             throw (SystemException, SoftException, SecurityException);
         
         /**
@@ -137,7 +147,11 @@ namespace suPHP {
          */
         void checkParentDirectories(const File& file,
                                     const UserInfo& owner,
-                                    const Configuration& config) const
+                                    const Configuration& config
+#ifdef COMMON_POOL
+                                    , const UserInfo& commonUser
+#endif
+				    ) const
             throw (SoftException);
 
 
diff -up suphp-0.7.1/src-o/Configuration.cpp suphp-0.7.1/src/Configuration.cpp
--- suphp-0.7.1/src-o/Configuration.cpp	2008-03-29 10:02:36.000000000 -0300
+++ suphp-0.7.1/src/Configuration.cpp	2010-06-11 23:52:34.000000000 -0300
@@ -112,6 +112,13 @@ suPHP::Configuration::Configuration() {
 #endif
     this->umask = 0077;
     this->chroot_path = "";
+    
+    this->allow_directory_owned_by_other=false;
+    
+#ifdef COMMON_POOL
+    this->common_user = "";
+    this->common_group = "";
+#endif // COMMON_POOL    
 }
 
 void suPHP::Configuration::readFromFile(File& file) 
@@ -157,6 +164,16 @@ void suPHP::Configuration::readFromFile(
                 this->umask = Util::octalStrToInt(value);
             else if (key == "chroot")
                 this->chroot_path = value;
+	    else if (key == "allow_directory_owned_by_other")
+		this->allow_directory_owned_by_other = this->strToBool(value);
+	    else if (key == "check_parent_owner") /* Compatibility with Dave D's patch - see http://www.mail-archive.com/suphp@lists.marsching.biz/msg00762.html*/
+		this->allow_directory_owned_by_other = !(this->strToBool(value));
+#ifdef COMMON_POOL
+            else if (key == "common_user")
+                this->common_user = value;
+            else if (key == "common_group")
+                this->common_group = value;
+#endif // COMMON_POOL
             else 
                 throw ParsingException("Unknown option \"" + key + 
                                        "\" in section [global]", 
@@ -250,3 +267,17 @@ int suPHP::Configuration::getUmask() con
 std::string suPHP::Configuration::getChrootPath() const {
     return this->chroot_path;
 }
+
+bool suPHP::Configuration::getAllowDirectoryOwnedByOther() const {
+    return this->allow_file_group_writeable;
+}
+
+#ifdef COMMON_POOL
+std::string suPHP::Configuration::getCommonUser() const {
+    return this->common_user;
+}
+
+std::string suPHP::Configuration::getCommonGroup() const {
+    return this->common_group;
+}
+#endif // COMMON_POOL
diff -up suphp-0.7.1/src-o/Configuration.hpp suphp-0.7.1/src/Configuration.hpp
--- suphp-0.7.1/src-o/Configuration.hpp	2008-03-29 10:02:36.000000000 -0300
+++ suphp-0.7.1/src/Configuration.hpp	2010-06-11 19:19:39.000000000 -0300
@@ -36,6 +36,8 @@ namespace suPHP {
 #include "KeyNotFoundException.hpp"
 #include "Logger.hpp"
 
+#include "config.h"
+
 namespace suPHP {
     /**
      * Class encapsulating run-time configuration.
@@ -58,6 +60,13 @@ namespace suPHP {
         int min_gid;
         int umask;
         std::string chroot_path;
+    
+	bool allow_directory_owned_by_other;
+    
+#ifdef COMMON_POOL
+	std::string common_user;
+	std::string common_group;
+#endif // COMMON_POOL
 
         /**
          * Converts string to bool
@@ -126,7 +135,7 @@ namespace suPHP {
         bool getAllowFileOthersWriteable() const;
 
         /**
-         * Returns wheter suPHP should ignore the others write bit of
+         * Returns whether suPHP should ignore the others write bit of
          * the directory the is script in
          */
         bool getAllowDirectoryOthersWriteable() const;
@@ -166,6 +175,24 @@ namespace suPHP {
          * Return chroot path
          */
         std::string getChrootPath() const;
+	
+        /**
+         * Returns whether suPHP should ignore the owner of
+         * the directory the is script in
+         */	
+	bool getAllowDirectoryOwnedByOther() const;
+
+#ifdef COMMON_POOL
+        /**
+         * Return common pool user
+         */
+        std::string getCommonUser() const;
+	
+        /**
+         * Return common pool group
+         */
+        std::string getCommonGroup() const;
+#endif // COMMON_POOL
     };
 };
 
