Author: markt
Date: Wed Feb 23 19:23:59 2011
New Revision: 1073891

URL: http://svn.apache.org/viewvc?rev=1073891&view=rev
Log:
Add the SecurityListener (disabled by default) that prevents Tomcat from 
starting if configured insecurely.
Added:
    tomcat/trunk/java/org/apache/catalina/security/Constants.java   (with props)
    tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java   
(with props)
Modified:
    tomcat/trunk/bin/catalina.sh
    tomcat/trunk/conf/server.xml
    tomcat/trunk/java/org/apache/catalina/security/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/security/SecurityUtil.java
    tomcat/trunk/res/confinstall/server_1.xml
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/listeners.xml
    tomcat/trunk/webapps/docs/setup.xml

Modified: tomcat/trunk/bin/catalina.sh
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/bin/catalina.sh?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/bin/catalina.sh (original)
+++ tomcat/trunk/bin/catalina.sh Wed Feb 23 19:23:59 2011
@@ -225,6 +225,10 @@ else
   JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER"
 fi
 
+# Uncomment the following line to make the umask available when using the
+# org.apache.catalina.security.SecurityListener
+#JAVA_OPTS="$JAVA_OPTS 
-Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`"
+
 # ----- Execute The Requested Command -----------------------------------------
 
 # Bugzilla 37848: only output this if we have a TTY

Modified: tomcat/trunk/conf/server.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/conf/server.xml?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/conf/server.xml (original)
+++ tomcat/trunk/conf/server.xml Wed Feb 23 19:23:59 2011
@@ -20,7 +20,9 @@
      Documentation at /docs/config/server.html
  -->
 <Server port="8005" shutdown="SHUTDOWN">
-
+  <!-- Security listener. Documentation at /docs/config/listeners.html
+  <Listener className="org.apache.catalina.security.SecurityListener" />
+  -->
   <!--APR library loader. Documentation at /docs/apr.html -->
   <Listener className="org.apache.catalina.core.AprLifecycleListener" 
SSLEngine="on" />
   <!--Initialize Jasper prior to webapps are loaded. Documentation at 
/docs/jasper-howto.html -->

Added: tomcat/trunk/java/org/apache/catalina/security/Constants.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/security/Constants.java?rev=1073891&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/security/Constants.java (added)
+++ tomcat/trunk/java/org/apache/catalina/security/Constants.java Wed Feb 23 
19:23:59 2011
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.security;
+
+public class Constants {
+
+    public static final String PACKAGE = "org.apache.catalina.security";
+
+    public static final String LINE_SEP = System.getProperty("line.separator");
+    public static final String CRLF = "\r\n";
+}

Propchange: tomcat/trunk/java/org/apache/catalina/security/Constants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/catalina/security/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/security/LocalStrings.properties?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/security/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/security/LocalStrings.properties Wed 
Feb 23 19:23:59 2011
@@ -14,4 +14,8 @@
 # limitations under the License.
 
 SecurityUtil.doAsPrivilege=An exception occurs when running the 
PrivilegedExceptionAction block.
-
+SecurityListener.checkUmaskFail=Start attempted with umask setting of [{0}]. 
Running Tomcat without a umask at least as restrictive as [{1}] has been 
blocked by the Lifecycle listener org.apache.catalina.security.SecurityListener 
(usually configured in CATALINA_BASE/conf/server.xml)
+SecurityListener.checkUmaskNone=No umask setting was found in system property 
[{0}]. However, it appears Tomcat is running on a platform that supports umask. 
The system property is typically set in CATALINA_HOME/bin/catalina.sh. The 
Lifecycle listener org.apache.catalina.security.SecurityListener (usually 
configured in CATALINA_BASE/conf/server.xml) expects a umask at least as 
restrictive as [{1}]
+SecurityListener.checkUmaskParseFail=Failed to parse value [{0}] as a valid 
umask.
+SecurityListener.checkUmaskSkip=Unable to determine umask. It appears Tomcat 
is running on Windows so skip the umask check.
+SecurityListener.checkUserWarning=Start attempted while running as user [{0}]. 
Running Tomcat as this user has been blocked by the Lifecycle listener 
org.apache.catalina.security.SecurityListener (usually configured in 
CATALINA_BASE/conf/server.xml)
\ No newline at end of file

Added: tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java?rev=1073891&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java (added)
+++ tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java Wed 
Feb 23 19:23:59 2011
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.security;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class SecurityListener implements LifecycleListener {
+
+    private static final Log log = LogFactory.getLog(SecurityListener.class);
+
+    private static final StringManager sm =
+        StringManager.getManager(Constants.PACKAGE);    
+
+    private static final String UMASK_PROPERTY_NAME =
+        Constants.PACKAGE + ".SecurityListener.UMASK";
+
+    private static final String UMASK_FORMAT = "%04o";
+
+    /**
+     * The list of operating system users not permitted to run Tomcat.
+     */
+    private Set<String> checkedOsUsers = new HashSet<String>();
+
+    /**
+     * The minimum umask that must be configured for the operating system user
+     * running Tomcat. The umask is handled as an octal.
+     */
+    private Integer minimumUmask = Integer.valueOf(7);
+
+
+    public SecurityListener() {
+        checkedOsUsers.add("root");
+    }
+
+
+    @Override
+    public void lifecycleEvent(LifecycleEvent event) {
+        // This is the earliest event in Lifecycle
+        if (event.getType().equals(Lifecycle.BEFORE_INIT_EVENT)) {
+            doChecks();
+        }
+    }
+
+
+    /**
+     * Set the list of operating system users not permitted to run Tomcat. By
+     * default, only root is prevented from running Tomcat. Calling this method
+     * with null or the empty string will clear the list of users and
+     * effectively disables this check. User names will always be checked in a
+     * case insensitive manner.
+     * 
+     * @param userList  A comma separated list of operating system users not
+     *                  permitted to run Tomcat
+     */
+    public void setCheckedOsUsers(String userNameList) {
+        if (userNameList == null || userNameList.length() == 0) {
+            checkedOsUsers.clear();
+        } else {
+            String[] userNames = userNameList.split(",");
+            for (String userName : userNames) {
+                if (userName.length() > 0) {
+                    checkedOsUsers.add(userName);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Returns the current list of operating system users not permitted to run
+     * Tomcat.
+     * 
+     * @return  A comma separated list of operating sytem user names.
+     */
+    public String getCheckedOsUsers() {
+        if (checkedOsUsers.size() == 0) {
+            return "";
+        }
+
+        StringBuilder result = new StringBuilder();
+        Iterator<String> iter = checkedOsUsers.iterator();
+        result.append(iter.next());
+        while (iter.hasNext()) {
+            result.append(',');
+            result.append(iter.next());
+        }
+        return result.toString();
+    }
+
+
+    /**
+     * Set the minimum umask that must be configured before Tomcat will start.
+     * 
+     * @param umask The 4-digit umask as returned by the OS command 
<i>umask</i>
+     */
+    public void setMinimumUmask(String umask) {
+        if (umask == null || umask.length() == 0) {
+            minimumUmask = Integer.valueOf(0);
+        } else {
+            minimumUmask = Integer.valueOf(umask, 8);
+        }
+    }
+
+
+    /**
+     * Get the minimum umask that must be configured before Tomcat will start.
+     * 
+     * @return  The 4-digit umask as used by the OS command <i>umask</i>
+     */
+    public String getMinimumUmask() {
+        return String.format(UMASK_FORMAT, minimumUmask);
+    }
+
+
+    /**
+     * Execute the security checks. Each check should be in a separate method.
+     */
+    protected void doChecks() {
+        checkOsUser();
+        checkUmask();
+    }
+    
+
+    protected void checkOsUser() {
+        String userName = System.getProperty("user.name");
+        if (userName != null) {
+            String userNameLC = userName.toLowerCase();
+        
+            if (checkedOsUsers.contains(userNameLC)) {
+                // Have to throw Error to force start process to be aborted
+                throw new Error(sm.getString(
+                        "SecurityListener.checkUserWarning", userName));
+            }
+        }
+    }
+    
+
+    protected void checkUmask() {
+        String prop = System.getProperty(UMASK_PROPERTY_NAME);
+        Integer umask = null;
+        if (prop != null) {
+            try {
+                 umask = Integer.valueOf(prop, 8);
+            } catch (NumberFormatException nfe) {
+                log.warn(sm.getString("SecurityListener.checkUmaskParseFail",
+                        prop));
+            }
+        }
+        if (umask == null) {
+            if (Constants.CRLF.equals(Constants.LINE_SEP)) {
+                // Probably running on Windows so no umask
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("SecurityListener.checkUmaskSkip"));
+                }
+                return;
+            } else {
+                if (minimumUmask.intValue() > 0) {
+                    log.warn(sm.getString(
+                            "SecurityListener.checkUmaskNone",
+                            UMASK_PROPERTY_NAME, getMinimumUmask()));
+                }
+                return;
+            }
+        }
+        
+        if ((umask.intValue() & minimumUmask.intValue()) !=
+                minimumUmask.intValue()) {
+            throw new Error(sm.getString("SecurityListener.checkUmaskFail",
+                    String.format(UMASK_FORMAT, umask), getMinimumUmask()));
+        }
+    }
+}

Propchange: tomcat/trunk/java/org/apache/catalina/security/SecurityListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/java/org/apache/catalina/security/SecurityUtil.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/security/SecurityUtil.java?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/security/SecurityUtil.java (original)
+++ tomcat/trunk/java/org/apache/catalina/security/SecurityUtil.java Wed Feb 23 
19:23:59 2011
@@ -73,8 +73,6 @@ public final class SecurityUtil{
     private static final org.apache.juli.logging.Log log=
         org.apache.juli.logging.LogFactory.getLog( SecurityUtil.class );
     
-    private static String PACKAGE = "org.apache.catalina.security";
-    
     private static boolean packageDefinitionEnabled =  
          (System.getProperty("package.definition") == null && 
            System.getProperty("package.access")  == null) ? false : true;
@@ -83,7 +81,7 @@ public final class SecurityUtil{
      * The string resources for this package.
      */
     private static final StringManager sm =
-        StringManager.getManager(PACKAGE);    
+        StringManager.getManager(Constants.PACKAGE);    
     
     
     /**

Modified: tomcat/trunk/res/confinstall/server_1.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/res/confinstall/server_1.xml?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/res/confinstall/server_1.xml (original)
+++ tomcat/trunk/res/confinstall/server_1.xml Wed Feb 23 19:23:59 2011
@@ -20,7 +20,9 @@
      Documentation at /docs/config/server.html
  -->
 <Server port="8005" shutdown="SHUTDOWN">
-
+  <!-- Security Listener. Documentation at /docs/config/listeners.html
+  <Listener className="org.apache.catalina.security.SecurityListener" />
+  -->
   <!--APR library loader. Documentation at /docs/apr.html -->
   <Listener className="org.apache.catalina.core.AprLifecycleListener" 
SSLEngine="on" />
   <!--Initialize Jasper prior to webapps are loaded. Documentation at 
/docs/jasper-howto.html -->

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Wed Feb 23 19:23:59 2011
@@ -53,6 +53,14 @@
         <bug>21669</bug>: Add the ability to specify the roleBase for the JNDI
         Realm as relative to the users DN. Based on a patch by Art W. (markt)
       </add>
+      <add>
+        <bug>22405</bug>: Add a new Lifecycle listener,
+        <code>org.apache.catalina.security.SecurityListener</code> that 
prevents
+        Tomcat from starting insecurely. It requires that Tomcat is not started
+        as root and that a umask at least as restrictive as 0007 is used. This
+        new listener is not enabled by default.
+        (markt)
+      </add>
       <fix>
         <bug>48863</bug>: Better logging when specifying an invalid directory
         for a class loader. Based on a patch by Ralf Hauser. (markt)

Modified: tomcat/trunk/webapps/docs/config/listeners.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/listeners.xml?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/listeners.xml (original)
+++ tomcat/trunk/webapps/docs/config/listeners.xml Wed Feb 23 19:23:59 2011
@@ -310,6 +310,39 @@ service:jmx:rmi://&lt;hostname&gt;:10002
 
     </attributes>
 
+    <h3>Security Lifecycle Listener 
(org.apache.catalina.security.SecurityListener)</h3>
+
+    <p>The <strong>Security Lifecycle Listener</strong> performs a number of
+    security checks when Tomcat starts and prevents Tomcat from starting if 
they
+    fail. The listener is not enabled by default. To enabled it uncomment the
+    listener in $CATALINA_BASE/conf/server.xml. If the operating system 
supports
+    umask then the line in $CATALINA_HOME/bin/catalina.sh that obtains the 
umask
+    also needs to be uncommented.</p>
+
+    <p>This listener must only be nested within <a 
href="server.html">Server</a>
+    elements.</p>
+
+    <p>The following additional attributes are supported by the 
<strong>Security
+    Lifecycle Listener</strong>:</p>
+
+    <attributes>
+
+      <attribute name="checkedOsUsers" required="false">
+        <p>A comma separated list of OS users that must not be used to start
+        Tomcat. If not specified, the default value of <b>root</b> is used. To
+        disable this check, set the attribute to the empty string. Usernames
+        are checked in a case-insensitive manner.</p>
+      </attribute>
+
+      <attribute name="minimumUmask" required="false">
+        <p>The least rectrictive umask that must be configured before Tomcat
+        will start. If not specified, the default value of <b>0007</b> is used.
+        To disable this check, set the attribute to the empty string. The check
+        is not performed on Windows platforms.</p>
+      </attribute>
+
+    </attributes>
+
   </subsection>
 
 </section>

Modified: tomcat/trunk/webapps/docs/setup.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/setup.xml?rev=1073891&r1=1073890&r2=1073891&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/setup.xml (original)
+++ tomcat/trunk/webapps/docs/setup.xml Wed Feb 23 19:23:59 2011
@@ -131,8 +131,12 @@
     <p>jsvc has other useful parameters, such as <code>-user</code> which 
        causes it to switch to another user after the daemon initialization is
        complete. This allows, for example, running Tomcat as a non privileged
-       user while still being able to use privileged ports. 
-       <code>jsvc --help</code> will return the full jsvc usage 
+       user while still being able to use privileged ports. Note that if you
+       use this option and start Tomcat as root, you&apos;ll need to disable 
the
+       <code>org.apache.catalina.security.SecurityListener</code> check that
+       prevents Tomcat starting when running as root.</p> 
+       
+    <p><code>jsvc --help</code> will return the full jsvc usage 
        information. In particular, the <code>-debug</code> option is useful
        to debug issues running jsvc.</p>
 



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to