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
200 doAsPrivilege(
201 methodName, targetObject, targetType, targetArguments, null);
202 }
203
204 /**
205 * Perform work as a particular <code>Subject</code>. Here the work
206 * will be granted to a <code>null</code> subject.
207 *
208 * @param methodName the method to apply the security restriction
209 * @param targetObject the <code>Filter</code> on which the method will
210 * be called.
211 * @param targetType <code>Class</code> array used to instanciate a
212 * <code>Method</code> object.
213 * @param targetArguments <code>Object</code> array contains the
214 * runtime parameters instance.
215 * @param principal the <code>Principal</code> to which the security
216 * privilege apply
217 */
218 public static void doAsPrivilege(final String methodName,
219 final Filter targetObject,
220 final Class[] targetType,
221 final Object[] targetArguments,
222 Principal principal)
223 throws java.lang.Exception{
224 Method method = null;
225
226 Method[] methodsCache = null;
227 if(objectCache.containsKey(targetObject)){
228 methodsCache = (Method[])objectCache.get(targetObject);
229 method = findMethod(methodsCache, methodName);
230 if (method == null){
231 method = createMethodAndCacheIt(methodsCache,
232 methodName,
233 targetObject,
234 targetType);
235 }
236 } else {
237 method = createMethodAndCacheIt(methodsCache,
238 methodName,
239 targetObject,
240 targetType);
241 }
242
243 execute(method, targetObject, targetArguments, principal);
244 }
245
246
247 /**
248 * Perform work as a particular </code>Subject</code>. Here the work
249 * will be granted to a <code>null</code> subject.
250 *
251 * @param methodName the method to apply the security restriction
252 * @param targetObject the <code>Servlet</code> on which the method will
253 * be called.
254 * @param targetArguments <code>Object</code> array contains the
255 * runtime parameters instance.
256 * @param principal the <code>Principal</code> to which the security
257 * privilege applies
258 */
259 private static void execute(final Method method,
260 final Object targetObject,
261 final Object[] targetArguments,
262 Principal principal)
263 throws java.lang.Exception{
264
265 try{
266 Subject subject = null;
267 PrivilegedExceptionAction pea = new PrivilegedExceptionAction(){
268 public Object run() throws Exception{
269 method.invoke(targetObject, targetArguments);
270 return null;
271 }
272 };
273
274 // The first argument is always the request object
275 if (targetArguments != null
276 && targetArguments[0] instanceof HttpServletRequest){
277 HttpServletRequest request =
278 (HttpServletRequest)targetArguments[0];
279
280 boolean hasSubject = false;
281 HttpSession session = request.getSession(false);
282 if (session != null){
283 subject =
284 (Subject)session.getAttribute(Globals.SUBJECT_ATTR);
285 hasSubject = (subject != null);
286 }
287
288 if (subject == null){
289 subject = new Subject();
290
291 if (principal != null){
292 subject.getPrincipals().add(principal);
293 }
294 }
295
296 if (session != null && !hasSubject) {
297 session.setAttribute(Globals.SUBJECT_ATTR, subject);
298 }
299 }
300
301 Subject.doAsPrivileged(subject, pea, null);
302 } catch( PrivilegedActionException pe) {
303 Throwable e;
304 if (pe.getException() instanceof InvocationTargetException) {
305 e = ((InvocationTargetException)pe.getException())
306 .getTargetException();
307 } else {
308 e = pe;
309 }
310
311 if (log.isDebugEnabled()){
312 log.debug(sm.getString("SecurityUtil.doAsPrivilege"), e);
313 }
314
315 if (e instanceof UnavailableException)
316 throw (UnavailableException) e;
317 else if (e instanceof ServletException)
318 throw (ServletException) e;
319 else if (e instanceof IOException)
320 throw (IOException) e;
321 else if (e instanceof RuntimeException)
322 throw (RuntimeException) e;
323 else
324 throw new ServletException(e.getMessage(), e);
325 }
326 }
327
328
329 /**
330 * Find a method stored within the cache.
331 * @param methodsCache the cache used to store method instance
332 * @param methodName the method to apply the security restriction
333 * @return the method instance, null if not yet created.
334 */
335 private static Method findMethod(Method[] methodsCache,
336 String methodName){
337 if (methodName.equalsIgnoreCase(INIT_METHOD)
338 && methodsCache[INIT] != null){
339 return methodsCache[INIT];
340 } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)
341 && methodsCache[DESTROY] != null){
342 return methodsCache[DESTROY];
343 } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)
344 && methodsCache[SERVICE] != null){
345 return methodsCache[SERVICE];
346 } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)
347 && methodsCache[DOFILTER] != null){
348 return methodsCache[DOFILTER];
349 }
350 return null;
351 }
352
353
354 /**
355 * Create the method and cache it for further re-use.
356 * @param methodsCache the cache used to store method instance
357 * @param methodName the method to apply the security restriction
358 * @param targetObject the <code>Servlet</code> on which the method will
359 * be called.
360 * @param targetType <code>Class</code> array used to instanciate a
361 * <code>Method</code> object.
362 * @return the method instance.
363 */
364 private static Method createMethodAndCacheIt(Method[] methodsCache,
365 String methodName,
366 Object targetObject,
367 Class[] targetType)
368 throws Exception{
369
370 if ( methodsCache == null){
371 methodsCache = new Method[3];
372 }
373
374 Method method =
375 targetObject.getClass().getMethod(methodName, targetType);
376
377 if (methodName.equalsIgnoreCase(INIT_METHOD)){
378 methodsCache[INIT] = method;
379 } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)){
380 methodsCache[DESTROY] = method;
381 } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)){
382 methodsCache[SERVICE] = method;
383 } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)){
384 methodsCache[DOFILTER] = method;
385 }
386
387 objectCache.put(targetObject, methodsCache );
388
389 return method;
390 }
391
392
393 /**
394 * Remove the object from the cache.
395 *
396 * @param cachedObject The object to remove
397 */
398 public static void remove(Object cachedObject){
399 objectCache.remove(cachedObject);
400 }
401
402
403 /**
404 * Return the <code>SecurityManager</code> only if Security is enabled AND
405 * package protection mechanism is enabled.
406 */
407 public static boolean isPackageProtectionEnabled(){
408 if (packageDefinitionEnabled && Globals.IS_SECURITY_ENABLED){
409 return true;
410 }
411 return false;
412 }
413
414
415 }