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.PACLClassUtil;
027    import com.liferay.portal.security.pacl.PACLPolicy;
028    import com.liferay.portal.security.pacl.PACLPolicyManager;
029    import com.liferay.portal.security.pacl.checker.CheckerUtil;
030    import com.liferay.portal.util.ClassLoaderUtil;
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 = ClassLoaderUtil.getClassLoader(clazz);
089    
090                    if (classClassLoader == null) {
091                            return;
092                    }
093    
094                    Class<?> callerClass = Reflection.getCallerClass(4);
095    
096                    ClassLoader callerClassLoader = ClassLoaderUtil.getClassLoader(
097                            callerClass);
098    
099                    if (callerClassLoader == null) {
100                            for (int i = 5;; i++) {
101                                    callerClass = Reflection.getCallerClass(i);
102    
103                                    if (callerClass == null) {
104                                            break;
105                                    }
106    
107                                    String className = callerClass.getName();
108    
109                                    if (!className.startsWith("java.lang") &&
110                                            !className.startsWith("java.security") &&
111                                            !className.startsWith("sun.reflect")) {
112    
113                                            callerClassLoader = ClassLoaderUtil.getClassLoader(
114                                                    callerClass);
115    
116                                            break;
117                                    }
118                            }
119                    }
120    
121                    if (classClassLoader == callerClassLoader) {
122                            return;
123                    }
124    
125                    Permission permission = new CheckMemberAccessPermission(
126                            PACLConstants.RUNTIME_PERMISSION_ACCESS_DECLARED_MEMBERS,
127                            callerClass, callerClassLoader, clazz, classClassLoader);
128    
129                    checkPermission(permission);
130            }
131    
132            @Override
133            public void checkPermission(Permission permission) {
134                    checkPermission(permission, null);
135            }
136    
137            @Override
138            public void checkPermission(Permission permission, Object context) {
139                    if (!PACLPolicyManager.isActive() ||
140                            !PortalSecurityManagerThreadLocal.isEnabled()) {
141    
142                            parentCheckPermission(permission, context);
143    
144                            return;
145                    }
146    
147                    PACLPolicy paclPolicy = PACLPolicyManager.getDefaultPACLPolicy();
148    
149                    if (!paclPolicy.isCheckablePermission(permission)) {
150                            parentCheckPermission(permission, context);
151    
152                            return;
153                    }
154    
155                    paclPolicy = getPACLPolicy(permission);
156    
157                    if ((paclPolicy == null) || !paclPolicy.isActive()) {
158                            parentCheckPermission(permission, context);
159    
160                            return;
161                    }
162    
163                    paclPolicy.checkPermission(permission);
164    
165                    parentCheckPermission(permission, context);
166            }
167    
168            protected PACLPolicy getPACLPolicy(Permission permission) {
169                    PACLPolicy paclPolicy =
170                            PortalSecurityManagerThreadLocal.getPACLPolicy();
171    
172                    if (paclPolicy != null) {
173                            return paclPolicy;
174                    }
175    
176                    if (permission instanceof PortalHookPermission) {
177                            PortalHookPermission portalHookPermission =
178                                    (PortalHookPermission)permission;
179    
180                            ClassLoader classLoader = portalHookPermission.getClassLoader();
181    
182                            paclPolicy = PACLPolicyManager.getPACLPolicy(classLoader);
183    
184                            if (paclPolicy == null) {
185                                    paclPolicy = PACLPolicyManager.getDefaultPACLPolicy();
186                            }
187    
188                            return paclPolicy;
189                    }
190                    else if (permission instanceof PortalRuntimePermission) {
191                            PortalRuntimePermission portalRuntimePermission =
192                                    (PortalRuntimePermission)permission;
193    
194                            String name = portalRuntimePermission.getName();
195    
196                            if (name.equals(
197                                            PACLConstants.PORTAL_RUNTIME_PERMISSION_EXPANDO_BRIDGE)) {
198    
199                                    return PACLClassUtil.getPACLPolicyByReflection(
200                                            true, _log.isDebugEnabled());
201                            }
202    
203                            /*if (name.equals(
204                                                    PACLConstants.
205                                                            PORTAL_RUNTIME_PERMISSION_SET_BEAN_PROPERTY)) {
206    
207                                    paclPolicy = PACLClassUtil.getPACLPolicyByReflection(
208                                            false, true);
209    
210                                    System.out.println("PACL policy " + paclPolicy);
211    
212                                    return paclPolicy;
213                            }*/
214                    }
215    
216                    return PACLClassUtil.getPACLPolicyByReflection(
217                            false, _log.isDebugEnabled());
218            }
219    
220            protected void initClasses() {
221    
222                    // Load dependent classes to prevent ClassCircularityError
223    
224                    _log.debug("Loading " + FileAvailabilityUtil.class.getName());
225                    _log.debug("Loading " + PortalHookPermission.class.getName());
226    
227                    // Touch dependent classes to prevent NoClassDefError
228    
229                    CheckerUtil.isAccessControllerDoPrivileged(0);
230                    PACLClassUtil.getPACLPolicyByReflection(false, false);
231            }
232    
233            protected void initInitialContextFactoryBuilder() throws Exception {
234                    if (!NamingManager.hasInitialContextFactoryBuilder()) {
235                            PACLInitialContextFactoryBuilder paclInitialContextFactoryBuilder =
236                                    new PACLInitialContextFactoryBuilder();
237    
238                            if (_log.isInfoEnabled()) {
239                                    _log.info("Overriding the initial context factory builder");
240                            }
241    
242                            NamingManager.setInitialContextFactoryBuilder(
243                                    paclInitialContextFactoryBuilder);
244                    }
245    
246                    Class<?> clazz = NamingManager.class;
247    
248                    String fieldName = "initctx_factory_builder";
249    
250                    if (JavaDetector.isIBM()) {
251                            fieldName = "icfb";
252                    }
253    
254                    Field field = clazz.getDeclaredField(fieldName);
255    
256                    field.setAccessible(true);
257    
258                    InitialContextFactoryBuilder initialContextFactoryBuilder =
259                            (InitialContextFactoryBuilder)field.get(null);
260    
261                    if (initialContextFactoryBuilder
262                                    instanceof PACLInitialContextFactoryBuilder) {
263    
264                            return;
265                    }
266    
267                    PACLInitialContextFactoryBuilder paclInitialContextFactoryBuilder =
268                            new PACLInitialContextFactoryBuilder();
269    
270                    paclInitialContextFactoryBuilder.setInitialContextFactoryBuilder(
271                            initialContextFactoryBuilder);
272    
273                    field.set(null, paclInitialContextFactoryBuilder);
274    
275                    if (_log.isInfoEnabled()) {
276                            _log.info(
277                                    "Overriding the initial context factory builder using " +
278                                            "reflection");
279                    }
280            }
281    
282            protected void parentCheckPermission(
283                    Permission permission, Object context) {
284    
285                    if (_parentSecurityManager != null) {
286                            if (context == null) {
287                                    context = getSecurityContext();
288                            }
289    
290                            _parentSecurityManager.checkPermission(permission, context);
291                    }
292            }
293    
294            private static Log _log = LogFactoryUtil.getLog(
295                    PortalSecurityManager.class.getName());
296    
297            private SecurityManager _parentSecurityManager;
298    
299    }