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://<hostname>: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'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