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
20 import java.io.IOException;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.security.Principal;
24 import java.security.PrivilegedActionException;
25 import java.security.PrivilegedExceptionAction;
26 import java.util.HashMap;
27
28 import javax.security.auth.Subject;
29 import javax.servlet.Filter;
30 import javax.servlet.Servlet;
31 import javax.servlet.ServletException;
32 import javax.servlet.UnavailableException;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpSession;
35
36 import org.apache.catalina.Globals;
37 import org.apache.catalina.util.StringManager;
38 /**
39 * This utility class associates a <code>Subject</code> to the current
40 * <code>AccessControlContext</code>. When a <code>SecurityManager</code> is
41 * used, * the container will always associate the called thread with an
42 * AccessControlContext * containing only the principal of the requested
43 * Servlet/Filter.
44 *
45 * This class uses reflection to invoke the invoke methods.
46 *
47 * @author Jean-Francois Arcand
48 */
49
50 public final class SecurityUtil{
51
52 private final static int INIT= 0;
53 private final static int SERVICE = 1;
54 private final static int DOFILTER = 1;
55 private final static int DESTROY = 2;
56
57 private final static String INIT_METHOD = "init";
58 private final static String DOFILTER_METHOD = "doFilter";
59 private final static String SERVICE_METHOD = "service";
60 private final static String DESTROY_METHOD = "destroy";
61
62 /**
63 * Cache every object for which we are creating method on it.
64 */
65 private static HashMap objectCache = new HashMap();
66
67 private static org.apache.juli.logging.Log log=
68 org.apache.juli.logging.LogFactory.getLog( SecurityUtil.class );
69
70 private static String PACKAGE = "org.apache.catalina.security";
71
72 private static boolean packageDefinitionEnabled =
73 (System.getProperty("package.definition") == null &&
74 System.getProperty("package.access") == null) ? false : true;
75
76 /**
77 * The string resources for this package.
78 */
79 private static final StringManager sm =
80 StringManager.getManager(PACKAGE);
81
82
83 /**
84 * Perform work as a particular </code>Subject</code>. Here the work
85 * will be granted to a <code>null</code> subject.
86 *
87 * @param methodName the method to apply the security restriction
88 * @param targetObject the <code>Servlet</code> on which the method will
89 * be called.
90 */
91 public static void doAsPrivilege(final String methodName,
92 final Servlet targetObject) throws java.lang.Exception{
93 doAsPrivilege(methodName, targetObject, null, null, null);
94 }
95
96
97 /**
98 * Perform work as a particular </code>Subject</code>. Here the work
99 * will be granted to a <code>null</code> subject.
100 *
101 * @param methodName the method to apply the security restriction
102 * @param targetObject the <code>Servlet</code> on which the method will
103 * be called.
104 * @param targetType <code>Class</code> array used to instanciate a i
105 * <code>Method</code> object.
106 * @param targetArguments <code>Object</code> array contains the runtime
107 * parameters instance.
108 */
109 public static void doAsPrivilege(final String methodName,
110 final Servlet targetObject,
111 final Class[] targetType,
112 final Object[] targetArguments)
113 throws java.lang.Exception{
114
115 doAsPrivilege(methodName,
116 targetObject,
117 targetType,
118 targetArguments,
119 null);
120 }
121
122
123 /**
124 * Perform work as a particular </code>Subject</code>. Here the work
125 * will be granted to a <code>null</code> subject.
126 *
127 * @param methodName the method to apply the security restriction
128 * @param targetObject the <code>Servlet</code> on which the method will
129 * be called.
130 * @param targetType <code>Class</code> array used to instanciate a
131 * <code>Method</code> object.
132 * @param targetArguments <code>Object</code> array contains the
133 * runtime parameters instance.
134 * @param principal the <code>Principal</code> to which the security
135 * privilege apply..
136 */
137 public static void doAsPrivilege(final String methodName,
138 final Servlet targetObject,
139 final Class[] targetType,
140 final Object[] targetArguments,
141 Principal principal)
142 throws java.lang.Exception{
143
144 Method method = null;
145 Method[] methodsCache = null;
146 if(objectCache.containsKey(targetObject)){
147 methodsCache = (Method[])objectCache.get(targetObject);
148 method = findMethod(methodsCache, methodName);
149 if (method == null){
150 method = createMethodAndCacheIt(methodsCache,
151 methodName,
152 targetObject,
153 targetType);
154 }
155 } else {
156 method = createMethodAndCacheIt(methodsCache,
157 methodName,
158 targetObject,
159 targetType);
160 }
161
162 execute(method, targetObject, targetArguments, principal);
163 }
164
165
166 /**
167 * Perform work as a particular </code>Subject</code>. Here the work
168 * will be granted to a <code>null</code> subject.
169 *
170 * @param methodName the method to apply the security restriction
171 * @param targetObject the <code>Filter</code> on which the method will
172 * be called.
173 */
174 public static void doAsPrivilege(final String methodName,
175 final Filter targetObject)
176 throws java.lang.Exception{
177
178 doAsPrivilege(methodName, targetObject, null, null);
179 }
180
181
182 /**
183 * Perform work as a particular </code>Subject</code>. Here the work
184 * will be granted to a <code>null</code> subject.
185 *
186 * @param methodName the method to apply the security restriction
187 * @param targetObject the <code>Filter</code> on which the method will
188 * be called.
189 * @param targetType <code>Class</code> array used to instanciate a
190 * <code>Method</code> object.
191 * @param targetArguments <code>Object</code> array contains the
192 * runtime parameters instance.
193 */
194 public static void doAsPrivilege(final String methodName,
195 final Filter targetObject,
196 final Class[] targetType,
197 final Object[] targetArguments)
198 throws java.lang.Exception{
199 Method method = null;
200
201 Method[] methodsCache = null;
202 if(objectCache.containsKey(targetObject)){
203 methodsCache = (Method[])objectCache.get(targetObject);
204 method = findMethod(methodsCache, methodName);
205 if (method == null){
206 method = createMethodAndCacheIt(methodsCache,
207 methodName,
208 targetObject,
209 targetType);
210 }
211 } else {
212 method = createMethodAndCacheIt(methodsCache,
213 methodName,
214 targetObject,
215 targetType);
216 }
217
218 execute(method, targetObject, targetArguments, null);
219 }
220
221
222 /**
223 * Perform work as a particular </code>Subject</code>. Here the work
224 * will be granted to a <code>null</code> subject.
225 *
226 * @param methodName the method to apply the security restriction
227 * @param targetObject the <code>Servlet</code> on which the method will
228 * be called.
229 * @param targetArguments <code>Object</code> array contains the
230 * runtime parameters instance.
231 * @param principal the <code>Principal</code> to which the security
232 * privilege applies
233 */
234 private static void execute(final Method method,
235 final Object targetObject,
236 final Object[] targetArguments,
237 Principal principal)
238 throws java.lang.Exception{
239
240 try{
241 Subject subject = null;
242 PrivilegedExceptionAction pea = new PrivilegedExceptionAction(){
243 public Object run() throws Exception{
244 method.invoke(targetObject, targetArguments);
245 return null;
246 }
247 };
248
249 // The first argument is always the request object
250 if (targetArguments != null
251 && targetArguments[0] instanceof HttpServletRequest){
252 HttpServletRequest request =
253 (HttpServletRequest)targetArguments[0];
254
255 boolean hasSubject = false;
256 HttpSession session = request.getSession(false);
257 if (session != null){
258 subject =
259 (Subject)session.getAttribute(Globals.SUBJECT_ATTR);
260 hasSubject = (subject != null);
261 }
262
263 if (subject == null){
264 subject = new Subject();
265
266 if (principal != null){
267 subject.getPrincipals().add(principal);
268 }
269 }
270
271 if (session != null && !hasSubject) {
272 session.setAttribute(Globals.SUBJECT_ATTR, subject);
273 }
274 }
275
276 Subject.doAsPrivileged(subject, pea, null);
277 } catch( PrivilegedActionException pe) {
278 Throwable e = ((InvocationTargetException)pe.getException())
279 .getTargetException();
280
281 if (log.isDebugEnabled()){
282 log.debug(sm.getString("SecurityUtil.doAsPrivilege"), e);
283 }
284
285 if (e instanceof UnavailableException)
286 throw (UnavailableException) e;
287 else if (e instanceof ServletException)
288 throw (ServletException) e;
289 else if (e instanceof IOException)
290 throw (IOException) e;
291 else if (e instanceof RuntimeException)
292 throw (RuntimeException) e;
293 else
294 throw new ServletException(e.getMessage(), e);
295 }
296 }
297
298
299 /**
300 * Find a method stored within the cache.
301 * @param methodsCache the cache used to store method instance
302 * @param methodName the method to apply the security restriction
303 * @return the method instance, null if not yet created.
304 */
305 private static Method findMethod(Method[] methodsCache,
306 String methodName){
307 if (methodName.equalsIgnoreCase(INIT_METHOD)
308 && methodsCache[INIT] != null){
309 return methodsCache[INIT];
310 } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)
311 && methodsCache[DESTROY] != null){
312 return methodsCache[DESTROY];
313 } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)
314 && methodsCache[SERVICE] != null){
315 return methodsCache[SERVICE];
316 } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)
317 && methodsCache[DOFILTER] != null){
318 return methodsCache[DOFILTER];
319 }
320 return null;
321 }
322
323
324 /**
325 * Create the method and cache it for further re-use.
326 * @param methodsCache the cache used to store method instance
327 * @param methodName the method to apply the security restriction
328 * @param targetObject the <code>Servlet</code> on which the method will
329 * be called.
330 * @param targetType <code>Class</code> array used to instanciate a
331 * <code>Method</code> object.
332 * @return the method instance.
333 */
334 private static Method createMethodAndCacheIt(Method[] methodsCache,
335 String methodName,
336 Object targetObject,
337 Class[] targetType)
338 throws Exception{
339
340 if ( methodsCache == null){
341 methodsCache = new Method[3];
342 }
343
344 Method method =
345 targetObject.getClass().getMethod(methodName, targetType);
346
347 if (methodName.equalsIgnoreCase(INIT_METHOD)){
348 methodsCache[INIT] = method;
349 } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)){
350 methodsCache[DESTROY] = method;
351 } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)){
352 methodsCache[SERVICE] = method;
353 } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)){
354 methodsCache[DOFILTER] = method;
355 }
356
357 objectCache.put(targetObject, methodsCache );
358
359 return method;
360 }
361
362
363 /**
364 * Remove the object from the cache.
365 *
366 * @param cachedObject The object to remove
367 */
368 public static void remove(Object cachedObject){
369 objectCache.remove(cachedObject);
370 }
371
372
373 /**
374 * Return the <code>SecurityManager</code> only if Security is enabled AND
375 * package protection mechanism is enabled.
376 */
377 public static boolean isPackageProtectionEnabled(){
378 if (packageDefinitionEnabled && Globals.IS_SECURITY_ENABLED){
379 return true;
380 }
381 return false;
382 }
383
384
385 }