001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.lang;
016    
017    import com.liferay.portal.jndi.pacl.PACLInitialContextFactoryBuilder;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.security.pacl.PACLConstants;
021    import com.liferay.portal.kernel.security.pacl.permission.CheckMemberAccessPermission;
022    import com.liferay.portal.kernel.security.pacl.permission.PortalHookPermission;
023    import com.liferay.portal.kernel.security.pacl.permission.PortalRuntimePermission;
024    import com.liferay.portal.kernel.servlet.taglib.FileAvailabilityUtil;
025    import com.liferay.portal.kernel.util.JavaDetector;
026    import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
027    import com.liferay.portal.security.pacl.PACLClassUtil;
028    import com.liferay.portal.security.pacl.PACLPolicy;
029    import com.liferay.portal.security.pacl.PACLPolicyManager;
030    import com.liferay.portal.security.pacl.checker.CheckerUtil;
031    
032    import java.lang.reflect.Field;
033    import java.lang.reflect.Member;
034    
035    import java.security.Permission;
036    
037    import javax.naming.spi.InitialContextFactoryBuilder;
038    import javax.naming.spi.NamingManager;
039    
040    import sun.reflect.Reflection;
041    
042    /**
043     * This is the portal's implementation of a security manager. The goal is to
044     * protect portal resources from plugins and prevent security issues by forcing
045     * plugin developers to openly declare their requirements. Where a
046     * SecurityManager exists, we set that as the parent and delegate to it as a
047     * fallback. This class will not delegate checks to super when there is no
048     * parent so as to avoid forcing the need for a default policy.
049     *
050     * @author Brian Wing Shun Chan
051     * @author Raymond Augé
052     * @author Zsolt Berentey
053     */
054    public class PortalSecurityManager extends SecurityManager {
055    
056            public PortalSecurityManager() {
057                    _parentSecurityManager = System.getSecurityManager();
058    
059                    initClasses();
060    
061                    try {
062                            initInitialContextFactoryBuilder();
063                    }
064                    catch (Exception e) {
065                            if (_log.isInfoEnabled()) {
066                                    _log.info(
067                                            "Unable to override the initial context factory builder " +
068                                                    "because one already exists. JNDI security is not " +
069                                                            "enabled.");
070                            }
071    
072                            if (_log.isWarnEnabled()) {
073                                    _log.warn(e, e);
074                            }
075                    }
076            }
077    
078            @Override
079            public void checkMemberAccess(Class<?> clazz, int which) {
080                    if (clazz == null) {
081                            throw new NullPointerException();
082                    }
083    
084                    if (which == Member.PUBLIC) {
085                            return;
086                    }
087    
088                    ClassLoader classClassLoader = PACLClassLoaderUtil.getClassLoader(
089                            clazz);
090    
091                    if (classClassLoader == null) {
092                            return;
093                    }
094    
095                    Class<?> callerClass = Reflection.getCallerClass(4);
096    
097                    ClassLoader callerClassLoader = PACLClassLoaderUtil.getClassLoader(
098                            callerClass);
099    
100                    if (callerClassLoader == null) {
101                            for (int i = 5;; i++) {
102                                    callerClass = Reflection.getCallerClass(i);
103    
104                                    if (callerClass == null) {
105                                            break;
106                                    }
107    
108                                    String className = callerClass.getName();
109    
110                                    if (!className.startsWith("java.lang") &&
111                                            !className.startsWith("java.security") &&
112                                            !className.startsWith("sun.reflect")) {
113    
114                                            callerClassLoader = PACLClassLoaderUtil.getClassLoader(
115                                                    callerClass);
116    
117                                            break;
118                                    }
119                            }
120                    }
121    
122                    if (classClassLoader == callerClassLoader) {
123                            return;
124                    }
125    
126                    Permission permission = new CheckMemberAccessPermission(
127                            PACLConstants.RUNTIME_PERMISSION_ACCESS_DECLARED_MEMBERS,
128                            callerClass, callerClassLoader, clazz, classClassLoader);
129    
130                    checkPermission(permission);
131            }
132    
133            @Override
134            public void checkPermission(Permission permission) {
135                    checkPermission(permission, null);
136            }
137    
138            @Override
139            public void checkPermission(Permission permission, Object context) {
140                    if (!PACLPolicyManager.isActive() ||
141                            !PortalSecurityManagerThreadLocal.isEnabled()) {
142    
143                            parentCheckPermission(permission, context);
144    
145                            return;
146                    }
147    
148                    PACLPolicy paclPolicy = PACLPolicyManager.getDefaultPACLPolicy();
149    
150                    if (!paclPolicy.isCheckablePermission(permission)) {
151                            parentCheckPermission(permission, context);
152    
153                            return;
154                    }
155    
156                    paclPolicy = getPACLPolicy(permission);
157    
158                    if ((paclPolicy == null) || !paclPolicy.isActive()) {
159                            parentCheckPermission(permission, context);
160    
161                            return;
162                    }
163    
164                    paclPolicy.checkPermission(permission);
165    
166                    parentCheckPermission(permission, context);
167            }
168    
169            protected PACLPolicy getPACLPolicy(Permission permission) {
170                    PACLPolicy paclPolicy =
171                            PortalSecurityManagerThreadLocal.getPACLPolicy();
172    
173                    if (paclPolicy != null) {
174                            return paclPolicy;
175                    }
176    
177                    if (permission instanceof PortalHookPermission) {
178                            PortalHookPermission portalHookPermission =
179                                    (PortalHookPermission)permission;
180    
181                            ClassLoader classLoader = portalHookPermission.getClassLoader();
182    
183                            paclPolicy = PACLPolicyManager.getPACLPolicy(classLoader);
184    
185                            if (paclPolicy == null) {
186                                    paclPolicy = PACLPolicyManager.getDefaultPACLPolicy();
187                            }
188    
189                            return paclPolicy;
190                    }
191                    else if (permission instanceof PortalRuntimePermission) {
192                            PortalRuntimePermission portalRuntimePermission =
193                                    (PortalRuntimePermission)permission;
194    
195                            String name = portalRuntimePermission.getName();
196    
197                            if (name.equals(
198                                            PACLConstants.PORTAL_RUNTIME_PERMISSION_EXPANDO_BRIDGE)) {
199    
200                                    return PACLClassUtil.getPACLPolicyByReflection(
201                                            true, _log.isDebugEnabled());
202                            }
203    
204                            /*if (name.equals(
205                                                    PACLConstants.
206                                                            PORTAL_RUNTIME_PERMISSION_SET_BEAN_PROPERTY)) {
207    
208                                    paclPolicy = PACLClassUtil.getPACLPolicyByReflection(
209                                            false, true);
210    
211                                    System.out.println("PACL policy " + paclPolicy);
212    
213                                    return paclPolicy;
214                            }*/
215                    }
216    
217                    return PACLClassUtil.getPACLPolicyByReflection(
218                            false, _log.isDebugEnabled());
219            }
220    
221            protected void initClasses() {
222    
223                    // Load dependent classes to prevent ClassCircularityError
224    
225                    _log.debug("Loading " + FileAvailabilityUtil.class.getName());
226                    _log.debug("Loading " + PortalHookPermission.class.getName());
227    
228                    // Touch dependent classes to prevent NoClassDefError
229    
230                    CheckerUtil.isAccessControllerDoPrivileged(0);
231                    PACLClassUtil.getPACLPolicyByReflection(false, false);
232            }
233    
234            protected void initInitialContextFactoryBuilder() throws Exception {
235                    if (!NamingManager.hasInitialContextFactoryBuilder()) {
236                            PACLInitialContextFactoryBuilder paclInitialContextFactoryBuilder =
237                                    new PACLInitialContextFactoryBuilder();
238    
239                            if (_log.isInfoEnabled()) {
240                                    _log.info("Overriding the initial context factory builder");
241                            }
242    
243                            NamingManager.setInitialContextFactoryBuilder(
244                                    paclInitialContextFactoryBuilder);
245                    }
246    
247                    Class<?> clazz = NamingManager.class;
248    
249                    String fieldName = "initctx_factory_builder";
250    
251                    if (JavaDetector.isIBM()) {
252                            fieldName = "icfb";
253                    }
254    
255                    Field field = clazz.getDeclaredField(fieldName);
256    
257                    field.setAccessible(true);
258    
259                    InitialContextFactoryBuilder initialContextFactoryBuilder =
260                            (InitialContextFactoryBuilder)field.get(null);
261    
262                    if (initialContextFactoryBuilder
263                                    instanceof PACLInitialContextFactoryBuilder) {
264    
265                            return;
266                    }
267    
268                    PACLInitialContextFactoryBuilder paclInitialContextFactoryBuilder =
269                            new PACLInitialContextFactoryBuilder();
270    
271                    paclInitialContextFactoryBuilder.setInitialContextFactoryBuilder(
272                            initialContextFactoryBuilder);
273    
274                    field.set(null, paclInitialContextFactoryBuilder);
275    
276                    if (_log.isInfoEnabled()) {
277                            _log.info(
278                                    "Overriding the initial context factory builder using " +
279                                            "reflection");
280                    }
281            }
282    
283            protected void parentCheckPermission(
284                    Permission permission, Object context) {
285    
286                    if (_parentSecurityManager != null) {
287                            if (context == null) {
288                                    context = getSecurityContext();
289                            }
290    
291                            _parentSecurityManager.checkPermission(permission, context);
292                    }
293            }
294    
295            private static Log _log = LogFactoryUtil.getLog(
296                    PortalSecurityManager.class.getName());
297    
298            private SecurityManager _parentSecurityManager;
299    
300    }