001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.catalina.security;
018:
019: import java.io.IOException;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.security.Principal;
023: import java.security.PrivilegedActionException;
024: import java.security.PrivilegedExceptionAction;
025: import java.util.HashMap;
026:
027: import javax.security.auth.Subject;
028: import javax.servlet.Filter;
029: import javax.servlet.Servlet;
030: import javax.servlet.ServletException;
031: import javax.servlet.UnavailableException;
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpSession;
034:
035: import org.apache.catalina.Globals;
036: import org.apache.catalina.util.StringManager;
037:
038: /**
039: * This utility class associates a <code>Subject</code> to the current
040: * <code>AccessControlContext</code>. When a <code>SecurityManager</code> is
041: * used, * the container will always associate the called thread with an
042: * AccessControlContext * containing only the principal of the requested
043: * Servlet/Filter.
044: *
045: * This class uses reflection to invoke the invoke methods.
046: *
047: * @author Jean-Francois Arcand
048: */
049:
050: public final class SecurityUtil {
051:
052: private final static int INIT = 0;
053: private final static int SERVICE = 1;
054: private final static int DOFILTER = 1;
055: private final static int DESTROY = 2;
056:
057: private final static String INIT_METHOD = "init";
058: private final static String DOFILTER_METHOD = "doFilter";
059: private final static String SERVICE_METHOD = "service";
060: private final static String DESTROY_METHOD = "destroy";
061:
062: /**
063: * Cache every object for which we are creating method on it.
064: */
065: private static HashMap objectCache = new HashMap();
066:
067: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
068: .getLog(SecurityUtil.class);
069:
070: private static String PACKAGE = "org.apache.catalina.security";
071:
072: private static boolean packageDefinitionEnabled = (System
073: .getProperty("package.definition") == null && System
074: .getProperty("package.access") == null) ? false : true;
075:
076: /**
077: * The string resources for this package.
078: */
079: private static final StringManager sm = StringManager
080: .getManager(PACKAGE);
081:
082: /**
083: * Perform work as a particular </code>Subject</code>. Here the work
084: * will be granted to a <code>null</code> subject.
085: *
086: * @param methodName the method to apply the security restriction
087: * @param targetObject the <code>Servlet</code> on which the method will
088: * be called.
089: */
090: public static void doAsPrivilege(final String methodName,
091: final Servlet targetObject) throws java.lang.Exception {
092: doAsPrivilege(methodName, targetObject, null, null, null);
093: }
094:
095: /**
096: * Perform work as a particular </code>Subject</code>. Here the work
097: * will be granted to a <code>null</code> subject.
098: *
099: * @param methodName the method to apply the security restriction
100: * @param targetObject the <code>Servlet</code> on which the method will
101: * be called.
102: * @param targetType <code>Class</code> array used to instanciate a i
103: * <code>Method</code> object.
104: * @param targetArguments <code>Object</code> array contains the runtime
105: * parameters instance.
106: */
107: public static void doAsPrivilege(final String methodName,
108: final Servlet targetObject, final Class[] targetType,
109: final Object[] targetArguments) throws java.lang.Exception {
110:
111: doAsPrivilege(methodName, targetObject, targetType,
112: targetArguments, null);
113: }
114:
115: /**
116: * Perform work as a particular </code>Subject</code>. Here the work
117: * will be granted to a <code>null</code> subject.
118: *
119: * @param methodName the method to apply the security restriction
120: * @param targetObject the <code>Servlet</code> on which the method will
121: * be called.
122: * @param targetType <code>Class</code> array used to instanciate a
123: * <code>Method</code> object.
124: * @param targetArguments <code>Object</code> array contains the
125: * runtime parameters instance.
126: * @param principal the <code>Principal</code> to which the security
127: * privilege apply..
128: */
129: public static void doAsPrivilege(final String methodName,
130: final Servlet targetObject, final Class[] targetType,
131: final Object[] targetArguments, Principal principal)
132: throws java.lang.Exception {
133:
134: Method method = null;
135: Method[] methodsCache = null;
136: if (objectCache.containsKey(targetObject)) {
137: methodsCache = (Method[]) objectCache.get(targetObject);
138: method = findMethod(methodsCache, methodName);
139: if (method == null) {
140: method = createMethodAndCacheIt(methodsCache,
141: methodName, targetObject, targetType);
142: }
143: } else {
144: method = createMethodAndCacheIt(methodsCache, methodName,
145: targetObject, targetType);
146: }
147:
148: execute(method, targetObject, targetArguments, principal);
149: }
150:
151: /**
152: * Perform work as a particular </code>Subject</code>. Here the work
153: * will be granted to a <code>null</code> subject.
154: *
155: * @param methodName the method to apply the security restriction
156: * @param targetObject the <code>Filter</code> on which the method will
157: * be called.
158: */
159: public static void doAsPrivilege(final String methodName,
160: final Filter targetObject) throws java.lang.Exception {
161:
162: doAsPrivilege(methodName, targetObject, null, null);
163: }
164:
165: /**
166: * Perform work as a particular </code>Subject</code>. Here the work
167: * will be granted to a <code>null</code> subject.
168: *
169: * @param methodName the method to apply the security restriction
170: * @param targetObject the <code>Filter</code> on which the method will
171: * be called.
172: * @param targetType <code>Class</code> array used to instanciate a
173: * <code>Method</code> object.
174: * @param targetArguments <code>Object</code> array contains the
175: * runtime parameters instance.
176: */
177: public static void doAsPrivilege(final String methodName,
178: final Filter targetObject, final Class[] targetType,
179: final Object[] targetArguments) throws java.lang.Exception {
180: Method method = null;
181:
182: Method[] methodsCache = null;
183: if (objectCache.containsKey(targetObject)) {
184: methodsCache = (Method[]) objectCache.get(targetObject);
185: method = findMethod(methodsCache, methodName);
186: if (method == null) {
187: method = createMethodAndCacheIt(methodsCache,
188: methodName, targetObject, targetType);
189: }
190: } else {
191: method = createMethodAndCacheIt(methodsCache, methodName,
192: targetObject, targetType);
193: }
194:
195: execute(method, targetObject, targetArguments, null);
196: }
197:
198: /**
199: * Perform work as a particular </code>Subject</code>. Here the work
200: * will be granted to a <code>null</code> subject.
201: *
202: * @param methodName the method to apply the security restriction
203: * @param targetObject the <code>Servlet</code> on which the method will
204: * be called.
205: * @param targetArguments <code>Object</code> array contains the
206: * runtime parameters instance.
207: * @param principal the <code>Principal</code> to which the security
208: * privilege applies
209: */
210: private static void execute(final Method method,
211: final Object targetObject, final Object[] targetArguments,
212: Principal principal) throws java.lang.Exception {
213:
214: try {
215: Subject subject = null;
216: PrivilegedExceptionAction pea = new PrivilegedExceptionAction() {
217: public Object run() throws Exception {
218: method.invoke(targetObject, targetArguments);
219: return null;
220: }
221: };
222:
223: // The first argument is always the request object
224: if (targetArguments != null
225: && targetArguments[0] instanceof HttpServletRequest) {
226: HttpServletRequest request = (HttpServletRequest) targetArguments[0];
227:
228: boolean hasSubject = false;
229: HttpSession session = request.getSession(false);
230: if (session != null) {
231: subject = (Subject) session
232: .getAttribute(Globals.SUBJECT_ATTR);
233: hasSubject = (subject != null);
234: }
235:
236: if (subject == null) {
237: subject = new Subject();
238:
239: if (principal != null) {
240: subject.getPrincipals().add(principal);
241: }
242: }
243:
244: if (session != null && !hasSubject) {
245: session.setAttribute(Globals.SUBJECT_ATTR, subject);
246: }
247: }
248:
249: Subject.doAsPrivileged(subject, pea, null);
250: } catch (PrivilegedActionException pe) {
251: Throwable e = ((InvocationTargetException) pe
252: .getException()).getTargetException();
253:
254: if (log.isDebugEnabled()) {
255: log
256: .debug(
257: sm
258: .getString("SecurityUtil.doAsPrivilege"),
259: e);
260: }
261:
262: if (e instanceof UnavailableException)
263: throw (UnavailableException) e;
264: else if (e instanceof ServletException)
265: throw (ServletException) e;
266: else if (e instanceof IOException)
267: throw (IOException) e;
268: else if (e instanceof RuntimeException)
269: throw (RuntimeException) e;
270: else
271: throw new ServletException(e.getMessage(), e);
272: }
273: }
274:
275: /**
276: * Find a method stored within the cache.
277: * @param methodsCache the cache used to store method instance
278: * @param methodName the method to apply the security restriction
279: * @return the method instance, null if not yet created.
280: */
281: private static Method findMethod(Method[] methodsCache,
282: String methodName) {
283: if (methodName.equalsIgnoreCase(INIT_METHOD)
284: && methodsCache[INIT] != null) {
285: return methodsCache[INIT];
286: } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)
287: && methodsCache[DESTROY] != null) {
288: return methodsCache[DESTROY];
289: } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)
290: && methodsCache[SERVICE] != null) {
291: return methodsCache[SERVICE];
292: } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)
293: && methodsCache[DOFILTER] != null) {
294: return methodsCache[DOFILTER];
295: }
296: return null;
297: }
298:
299: /**
300: * Create the method and cache it for further re-use.
301: * @param methodsCache the cache used to store method instance
302: * @param methodName the method to apply the security restriction
303: * @param targetObject the <code>Servlet</code> on which the method will
304: * be called.
305: * @param targetType <code>Class</code> array used to instanciate a
306: * <code>Method</code> object.
307: * @return the method instance.
308: */
309: private static Method createMethodAndCacheIt(Method[] methodsCache,
310: String methodName, Object targetObject, Class[] targetType)
311: throws Exception {
312:
313: if (methodsCache == null) {
314: methodsCache = new Method[3];
315: }
316:
317: Method method = targetObject.getClass().getMethod(methodName,
318: targetType);
319:
320: if (methodName.equalsIgnoreCase(INIT_METHOD)) {
321: methodsCache[INIT] = method;
322: } else if (methodName.equalsIgnoreCase(DESTROY_METHOD)) {
323: methodsCache[DESTROY] = method;
324: } else if (methodName.equalsIgnoreCase(SERVICE_METHOD)) {
325: methodsCache[SERVICE] = method;
326: } else if (methodName.equalsIgnoreCase(DOFILTER_METHOD)) {
327: methodsCache[DOFILTER] = method;
328: }
329:
330: objectCache.put(targetObject, methodsCache);
331:
332: return method;
333: }
334:
335: /**
336: * Remove the object from the cache.
337: *
338: * @param cachedObject The object to remove
339: */
340: public static void remove(Object cachedObject) {
341: objectCache.remove(cachedObject);
342: }
343:
344: /**
345: * Return the <code>SecurityManager</code> only if Security is enabled AND
346: * package protection mechanism is enabled.
347: */
348: public static boolean isPackageProtectionEnabled() {
349: if (packageDefinitionEnabled && Globals.IS_SECURITY_ENABLED) {
350: return true;
351: }
352: return false;
353: }
354:
355: }
|