View | Details | Raw Unified | Return to bug 22405
Collapse All | Expand All

(-)bin/catalina.sh (+4 lines)
Lines 276-281 Link Here
276
        -sourcepath "$CATALINA_HOME"/../../java \
276
        -sourcepath "$CATALINA_HOME"/../../java \
277
        -Djava.security.manager \
277
        -Djava.security.manager \
278
        -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
278
        -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
279
        -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask` \
279
        -Dcatalina.base="$CATALINA_BASE" \
280
        -Dcatalina.base="$CATALINA_BASE" \
280
        -Dcatalina.home="$CATALINA_HOME" \
281
        -Dcatalina.home="$CATALINA_HOME" \
281
        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
282
        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
Lines 284-289 Link Here
284
      exec "$_RUNJDB" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
285
      exec "$_RUNJDB" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
285
        -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
286
        -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
286
        -sourcepath "$CATALINA_HOME"/../../java \
287
        -sourcepath "$CATALINA_HOME"/../../java \
288
        -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask` \
287
        -Dcatalina.base="$CATALINA_BASE" \
289
        -Dcatalina.base="$CATALINA_BASE" \
288
        -Dcatalina.home="$CATALINA_HOME" \
290
        -Dcatalina.home="$CATALINA_HOME" \
289
        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
291
        -Djava.io.tmpdir="$CATALINA_TMPDIR" \
Lines 303-308 Link Here
303
      -Djava.endorsed.dirs=\"$JAVA_ENDORSED_DIRS\" -classpath \"$CLASSPATH\" \
305
      -Djava.endorsed.dirs=\"$JAVA_ENDORSED_DIRS\" -classpath \"$CLASSPATH\" \
304
      -Djava.security.manager \
306
      -Djava.security.manager \
305
      -Djava.security.policy==\"$CATALINA_BASE/conf/catalina.policy\" \
307
      -Djava.security.policy==\"$CATALINA_BASE/conf/catalina.policy\" \
308
      -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask` \
306
      -Dcatalina.base=\"$CATALINA_BASE\" \
309
      -Dcatalina.base=\"$CATALINA_BASE\" \
307
      -Dcatalina.home=\"$CATALINA_HOME\" \
310
      -Dcatalina.home=\"$CATALINA_HOME\" \
308
      -Djava.io.tmpdir=\"$CATALINA_TMPDIR\" \
311
      -Djava.io.tmpdir=\"$CATALINA_TMPDIR\" \
Lines 310-315 Link Here
310
  else
313
  else
311
    eval \"$_RUNJAVA\" \"$LOGGING_CONFIG\" $JAVA_OPTS $CATALINA_OPTS \
314
    eval \"$_RUNJAVA\" \"$LOGGING_CONFIG\" $JAVA_OPTS $CATALINA_OPTS \
312
      -Djava.endorsed.dirs=\"$JAVA_ENDORSED_DIRS\" -classpath \"$CLASSPATH\" \
315
      -Djava.endorsed.dirs=\"$JAVA_ENDORSED_DIRS\" -classpath \"$CLASSPATH\" \
316
      -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask` \
313
      -Dcatalina.base=\"$CATALINA_BASE\" \
317
      -Dcatalina.base=\"$CATALINA_BASE\" \
314
      -Dcatalina.home=\"$CATALINA_HOME\" \
318
      -Dcatalina.home=\"$CATALINA_HOME\" \
315
      -Djava.io.tmpdir=\"$CATALINA_TMPDIR\" \
319
      -Djava.io.tmpdir=\"$CATALINA_TMPDIR\" \
(-)conf/server.xml (+1 lines)
Lines 21-26 Link Here
21
 -->
21
 -->
22
<Server port="8005" shutdown="SHUTDOWN">
22
<Server port="8005" shutdown="SHUTDOWN">
23
23
24
  <Listener className="org.apache.catalina.security.SecurityListener" />
24
  <!--APR library loader. Documentation at /docs/apr.html -->
25
  <!--APR library loader. Documentation at /docs/apr.html -->
25
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
26
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
26
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
27
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
(-)java/org/apache/catalina/security/Constants.java (+25 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.catalina.security;
18
19
public class Constants {
20
21
    public static final String PACKAGE = "org.apache.catalina.security";
22
23
    public static final String LINE_SEP = System.getProperty("line.separator");
24
    public static final String CRLF = "\r\n";
25
}
0
  + native
26
  + native
(-)java/org/apache/catalina/security/LocalStrings.properties (-1 / +4 lines)
Lines 14-17 Link Here
14
# limitations under the License.
14
# limitations under the License.
15
15
16
SecurityUtil.doAsPrivilege=An exception occurs when running the PrivilegedExceptionAction block.
16
SecurityUtil.doAsPrivilege=An exception occurs when running the PrivilegedExceptionAction block.
17
17
SecurityListener.checkUmaskFail=Start attempted with umask setting of [{0}]. Running Tomcat without a umask at least as restrictive as [{}] has been blocked by the Lifecycle listener org.apache.catalina.security.SecurityListener (usually configured in CATALINA_BASE/conf/server.xml)
18
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}]
19
SecurityListener.checkUmaskSkip=Unable to determine umask. It appears Tomcat is running on Windows so skip the umask check.
20
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)
(-)java/org/apache/catalina/security/SecurityListener.java (+186 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.catalina.security;
18
19
import java.util.HashSet;
20
import java.util.Iterator;
21
import java.util.Set;
22
23
import org.apache.catalina.Lifecycle;
24
import org.apache.catalina.LifecycleEvent;
25
import org.apache.catalina.LifecycleListener;
26
import org.apache.juli.logging.Log;
27
import org.apache.juli.logging.LogFactory;
28
import org.apache.tomcat.util.res.StringManager;
29
30
public class SecurityListener implements LifecycleListener {
31
32
    private static final Log log = LogFactory.getLog(SecurityListener.class);
33
34
    private static final StringManager sm =
35
        StringManager.getManager(Constants.PACKAGE);    
36
37
    private static final String UMASK_PROPERTY_NAME =
38
        Constants.PACKAGE + ".SecurityListener.UMASK";
39
40
    private static final String UMASK_FORMAT = "%04o";
41
42
    /**
43
     * The list of operating system users not permitted to run Tomcat.
44
     */
45
    private Set<String> checkedOsUsers = new HashSet<String>();
46
47
    /**
48
     * The minimum umask that must be configured for the operating system user
49
     * running Tomcat. The umask is handled as an octal.
50
     */
51
    private Integer minimumUmask = Integer.valueOf(7);
52
53
54
    public SecurityListener() {
55
        checkedOsUsers.add("root");
56
    }
57
58
59
    @Override
60
    public void lifecycleEvent(LifecycleEvent event) {
61
        // This is the earliest event in Lifecycle
62
        if (event.getType().equals(Lifecycle.BEFORE_INIT_EVENT)) {
63
            doChecks();
64
        }
65
    }
66
67
68
    /**
69
     * Set the list of operating system users not permitted to run Tomcat. By
70
     * default, only root is prevented from running Tomcat. Calling this method
71
     * with null or the empty string will clear the list of users and
72
     * effectively disables this check. User names will always be checked in a
73
     * case insensitive manner.
74
     * 
75
     * @param userList  A comma separated list of operating system users not
76
     *                  permitted to run Tomcat
77
     */
78
    public void setCheckedOsUsers(String userNameList) {
79
        if (userNameList == null || userNameList.length() == 0) {
80
            checkedOsUsers.clear();
81
        } else {
82
            String[] userNames = userNameList.split(",");
83
            for (String userName : userNames) {
84
                if (userName.length() > 0) {
85
                    checkedOsUsers.add(userName);
86
                }
87
            }
88
        }
89
    }
90
91
92
    /**
93
     * Returns the current list of operating system users not permitted to run
94
     * Tomcat.
95
     * 
96
     * @return  A comma separated list of operating sytem user names.
97
     */
98
    public String getCheckedOsUsers() {
99
        if (checkedOsUsers.size() == 0) {
100
            return "";
101
        }
102
103
        StringBuilder result = new StringBuilder();
104
        Iterator<String> iter = checkedOsUsers.iterator();
105
        result.append(iter.next());
106
        while (iter.hasNext()) {
107
            result.append(',');
108
            result.append(iter.next());
109
        }
110
        return result.toString();
111
    }
112
113
114
    /**
115
     * Set the minimum umask that must be configured before Tomcat will start.
116
     * 
117
     * @param umask The 4-digit umask as returned by the OS command <i>umask</i>
118
     */
119
    public void setMinimumUmask(String umask) {
120
        if (umask == null || umask.length() == 0) {
121
            minimumUmask = Integer.valueOf(0);
122
        } else {
123
            minimumUmask = Integer.valueOf(umask, 8);
124
        }
125
    }
126
127
128
    /**
129
     * Get the minimum umask that must be configured before Tomcat will start.
130
     * 
131
     * @return  The 4-digit umask as used by the OS command <i>umask</i>
132
     */
133
    public String getMinimumUmask() {
134
        return String.format(UMASK_FORMAT, minimumUmask);
135
    }
136
137
138
    /**
139
     * Execute the security checks. Each check should be in a separate method.
140
     */
141
    protected void doChecks() {
142
        checkOsUser();
143
        checkUmask();
144
    }
145
    
146
147
    protected void checkOsUser() {
148
        String userName = System.getProperty("user.name");
149
        if (userName != null) {
150
            String userNameLC = userName.toLowerCase();
151
        
152
            if (checkedOsUsers.contains(userNameLC)) {
153
                // Have to throw Error to force start process to be aborted
154
                throw new Error(sm.getString(
155
                        "SecurityListener.checkUserWarning", userName));
156
            }
157
        }
158
    }
159
    
160
161
    protected void checkUmask() {
162
        Integer umask = Integer.getInteger(UMASK_PROPERTY_NAME, null);
163
        if (umask == null) {
164
            if (Constants.CRLF.equals(Constants.LINE_SEP)) {
165
                // Probably running on Windows so no umask
166
                if (log.isDebugEnabled()) {
167
                    log.debug(sm.getString("SecurityListener.checkUmaskSkip"));
168
                }
169
                return;
170
            } else {
171
                if (minimumUmask.intValue() > 0) {
172
                    log.warn(sm.getString(
173
                            "SecurityListener.checkUmaskNone",
174
                            UMASK_PROPERTY_NAME, getMinimumUmask()));
175
                }
176
                return;
177
            }
178
        }
179
        
180
        if ((umask.intValue() & minimumUmask.intValue()) !=
181
                minimumUmask.intValue()) {
182
            throw new Error(sm.getString("SecurityListener.checkUmaskFail",
183
                    String.format(UMASK_FORMAT, umask), getMinimumUmask()));
184
        }
185
    }
186
}
0
  + native
187
  + native
(-)java/org/apache/catalina/security/SecurityUtil.java (-3 / +1 lines)
Lines 73-80 Link Here
73
    private static final org.apache.juli.logging.Log log=
73
    private static final org.apache.juli.logging.Log log=
74
        org.apache.juli.logging.LogFactory.getLog( SecurityUtil.class );
74
        org.apache.juli.logging.LogFactory.getLog( SecurityUtil.class );
75
    
75
    
76
    private static String PACKAGE = "org.apache.catalina.security";
77
    
78
    private static boolean packageDefinitionEnabled =  
76
    private static boolean packageDefinitionEnabled =  
79
         (System.getProperty("package.definition") == null && 
77
         (System.getProperty("package.definition") == null && 
80
           System.getProperty("package.access")  == null) ? false : true;
78
           System.getProperty("package.access")  == null) ? false : true;
Lines 83-89 Link Here
83
     * The string resources for this package.
81
     * The string resources for this package.
84
     */
82
     */
85
    private static final StringManager sm =
83
    private static final StringManager sm =
86
        StringManager.getManager(PACKAGE);    
84
        StringManager.getManager(Constants.PACKAGE);    
87
    
85
    
88
    
86
    
89
    /**
87
    /**
(-)res/confinstall/server_1.xml (+1 lines)
Lines 21-26 Link Here
21
 -->
21
 -->
22
<Server port="8005" shutdown="SHUTDOWN">
22
<Server port="8005" shutdown="SHUTDOWN">
23
23
24
  <Listener className="org.apache.catalina.security.SecurityListener" />
24
  <!--APR library loader. Documentation at /docs/apr.html -->
25
  <!--APR library loader. Documentation at /docs/apr.html -->
25
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
26
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
26
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
27
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
(-)webapps/docs/changelog.xml (+7 lines)
Lines 54-59 Link Here
54
        length names being provided for users, roles and groups in the
54
        length names being provided for users, roles and groups in the
55
        <code>MemoryRealm</code> and <code>UserDatabaseRealm</code>. (markt)
55
        <code>MemoryRealm</code> and <code>UserDatabaseRealm</code>. (markt)
56
      </add>
56
      </add>
57
      <add>
58
        <bug>22405</bug>: Add a new Lifecycle listener,
59
        <code>org.apache.catalina.security.SecurityListener</code> that, by
60
        default, prevents Tomcat from starting insecurely. It requires that
61
        Tomcat is not started as root and that a umask at least as restrictive
62
        as 0007 is used. (markt)
63
      </add>
57
      <update>
64
      <update>
58
        Improve fix for <bug>50205</bug> to trigger an error earlier if invalid
65
        Improve fix for <bug>50205</bug> to trigger an error earlier if invalid
59
        configuration is used. (markt)
66
        configuration is used. (markt)
(-)webapps/docs/setup.xml (-2 / +6 lines)
Lines 131-138 Link Here
131
    <p>jsvc has other useful parameters, such as <code>-user</code> which 
131
    <p>jsvc has other useful parameters, such as <code>-user</code> which 
132
       causes it to switch to another user after the daemon initialization is
132
       causes it to switch to another user after the daemon initialization is
133
       complete. This allows, for example, running Tomcat as a non privileged
133
       complete. This allows, for example, running Tomcat as a non privileged
134
       user while still being able to use privileged ports. 
134
       user while still being able to use privileged ports. Note that if you
135
       <code>jsvc --help</code> will return the full jsvc usage 
135
       use this option and start Tomcat as root, you&apos;ll need to disable the
136
       <code>org.apache.catalina.security.SecurityListener</code> check that
137
       prevents Tomcat starting when running as root.</p> 
138
       
139
    <p><code>jsvc --help</code> will return the full jsvc usage 
136
       information. In particular, the <code>-debug</code> option is useful
140
       information. In particular, the <code>-debug</code> option is useful
137
       to debug issues running jsvc.</p>
141
       to debug issues running jsvc.</p>
138
142
(-)webapps/docs/config/listeners.xml (+30 lines)
Lines 333-338 Link Here
333
333
334
    </attributes>
334
    </attributes>
335
335
336
    <h3>Security Lifecycle Listener (org.apache.catalina.security.SecurityListener)</h3>
337
338
    <p>The <strong>Security Lifecycle Listener</strong> performs a number of
339
    security checks when Tomcat starts and prevents Tomcat from starting if they
340
    fail.</p>
341
342
    <p>This listener must only be nested within <a href="server.html">Server</a>
343
    elements.</p>
344
345
    <p>The following additional attributes are supported by the <strong>Security
346
    Lifecycle Listener</strong>:</p>
347
348
    <attributes>
349
350
      <attribute name="checkedOsUsers" required="false">
351
        <p>A comma separated list of OS users that must not be used to start
352
        Tomcat. If not specified, the default value of <b>root</b> is used. To
353
        disable this check, set the attribute to the empty string. Usernames
354
        are checked in a case-insensitive manner.</p>
355
      </attribute>
356
357
      <attribute name="minimumUmask" required="false">
358
        <p>The least rectrictive umask that must be configured before Tomcat
359
        will start. If not specified, the default value of <b>0007</b> is used.
360
        To disable this check, set the attribute to the empty string. The check
361
        is not performed on Windows platforms.</p>
362
      </attribute>
363
364
    </attributes>
365
336
  </subsection>
366
  </subsection>
337
367
338
</section>
368
</section>

Return to bug 22405