--- /dev/null
+/*
+ * 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()));
+ }
+ }
+}
</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>