From: markt Date: Wed, 23 Feb 2011 19:23:59 +0000 (+0000) Subject: Add the SecurityListener (disabled by default) that prevents Tomcat from starting... X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=e996318237d569322020ce6b9c5b6161eb8fbeab;p=tomcat7.0 Add the SecurityListener (disabled by default) that prevents Tomcat from starting if configured insecurely. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1073891 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/bin/catalina.sh b/bin/catalina.sh index 0bfd6c27b..e07d0ec73 100755 --- a/bin/catalina.sh +++ b/bin/catalina.sh @@ -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 diff --git a/conf/server.xml b/conf/server.xml index b98069449..e993fcb51 100644 --- a/conf/server.xml +++ b/conf/server.xml @@ -20,7 +20,9 @@ Documentation at /docs/config/server.html --> - + diff --git a/java/org/apache/catalina/security/Constants.java b/java/org/apache/catalina/security/Constants.java new file mode 100644 index 000000000..ac4268318 --- /dev/null +++ b/java/org/apache/catalina/security/Constants.java @@ -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"; +} diff --git a/java/org/apache/catalina/security/LocalStrings.properties b/java/org/apache/catalina/security/LocalStrings.properties index 0a9e096f1..a8439bf26 100644 --- a/java/org/apache/catalina/security/LocalStrings.properties +++ b/java/org/apache/catalina/security/LocalStrings.properties @@ -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 diff --git a/java/org/apache/catalina/security/SecurityListener.java b/java/org/apache/catalina/security/SecurityListener.java new file mode 100644 index 000000000..c4ea14994 --- /dev/null +++ b/java/org/apache/catalina/security/SecurityListener.java @@ -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 checkedOsUsers = new HashSet(); + + /** + * 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 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 umask + */ + 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 umask + */ + 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())); + } + } +} diff --git a/java/org/apache/catalina/security/SecurityUtil.java b/java/org/apache/catalina/security/SecurityUtil.java index ae4a3bccb..0e1eddb8f 100644 --- a/java/org/apache/catalina/security/SecurityUtil.java +++ b/java/org/apache/catalina/security/SecurityUtil.java @@ -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); /** diff --git a/res/confinstall/server_1.xml b/res/confinstall/server_1.xml index 354581203..9198f5e95 100644 --- a/res/confinstall/server_1.xml +++ b/res/confinstall/server_1.xml @@ -20,7 +20,9 @@ Documentation at /docs/config/server.html --> - + diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 21eaab9b9..5e9415681 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -53,6 +53,14 @@ 21669: 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) + + 22405: Add a new Lifecycle listener, + org.apache.catalina.security.SecurityListener 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) + 48863: Better logging when specifying an invalid directory for a class loader. Based on a patch by Ralf Hauser. (markt) diff --git a/webapps/docs/config/listeners.xml b/webapps/docs/config/listeners.xml index ba3d25864..71dd84d56 100644 --- a/webapps/docs/config/listeners.xml +++ b/webapps/docs/config/listeners.xml @@ -310,6 +310,39 @@ service:jmx:rmi://<hostname>:10002/jndi/rmi://<hostname>:10001/jmxrm +

Security Lifecycle Listener (org.apache.catalina.security.SecurityListener)

+ +

The Security Lifecycle Listener 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.

+ +

This listener must only be nested within Server + elements.

+ +

The following additional attributes are supported by the Security + Lifecycle Listener:

+ + + + +

A comma separated list of OS users that must not be used to start + Tomcat. If not specified, the default value of root is used. To + disable this check, set the attribute to the empty string. Usernames + are checked in a case-insensitive manner.

+
+ + +

The least rectrictive umask that must be configured before Tomcat + will start. If not specified, the default value of 0007 is used. + To disable this check, set the attribute to the empty string. The check + is not performed on Windows platforms.

+
+ +
+ diff --git a/webapps/docs/setup.xml b/webapps/docs/setup.xml index cedc4c1d9..a407adc79 100644 --- a/webapps/docs/setup.xml +++ b/webapps/docs/setup.xml @@ -131,8 +131,12 @@

jsvc has other useful parameters, such as -user 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. - jsvc --help 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 + org.apache.catalina.security.SecurityListener check that + prevents Tomcat starting when running as root.

+ +

jsvc --help will return the full jsvc usage information. In particular, the -debug option is useful to debug issues running jsvc.