| /* |
| * 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.Locale; |
| 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 final Set<String> 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 using the system default Locale. |
| * |
| * @param userNameList 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.toLowerCase(Locale.getDefault())); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * 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(Locale.getDefault()); |
| |
| 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(System.lineSeparator())) { |
| // 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())); |
| } |
| } |
| } |