001
014
015 package com.liferay.portal.security.pacl.checker;
016
017 import com.liferay.portal.bean.BeanLocatorImpl;
018 import com.liferay.portal.dao.orm.hibernate.DynamicQueryFactoryImpl;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.portlet.PortletClassLoaderUtil;
022 import com.liferay.portal.kernel.security.pacl.permission.PortalRuntimePermission;
023 import com.liferay.portal.kernel.util.GetterUtil;
024 import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
025 import com.liferay.portal.kernel.util.SetUtil;
026 import com.liferay.portal.kernel.util.StringBundler;
027 import com.liferay.portal.kernel.util.StringPool;
028 import com.liferay.portal.kernel.util.StringUtil;
029 import com.liferay.portal.kernel.util.Validator;
030 import com.liferay.portal.service.BaseLocalServiceImpl;
031 import com.liferay.portal.service.BaseServiceImpl;
032 import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
033 import com.liferay.portal.template.TemplateContextHelper;
034
035 import java.lang.reflect.Modifier;
036
037 import java.security.Permission;
038
039 import java.util.ArrayList;
040 import java.util.HashMap;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.Properties;
044 import java.util.Set;
045 import java.util.TreeSet;
046 import java.util.regex.Matcher;
047 import java.util.regex.Pattern;
048
049 import sun.reflect.Reflection;
050
051
055 public class PortalRuntimeChecker extends BaseChecker {
056
057 public void afterPropertiesSet() {
058 initClassLoaderReferenceIds();
059 initExpandoBridgeClassNames();
060 initGetBeanPropertyClassNames();
061 initPortletBagPoolPortletIds();
062 initSearchEngineIds();
063 initSetBeanPropertyClassNames();
064 initThreadPoolExecutorNames();
065 }
066
067 @Override
068 public AuthorizationProperty generateAuthorizationProperty(
069 Object... arguments) {
070
071 if ((arguments == null) || (arguments.length != 1) ||
072 !(arguments[0] instanceof Permission)) {
073
074 return null;
075 }
076
077 PortalRuntimePermission portalRuntimePermission =
078 (PortalRuntimePermission)arguments[0];
079
080 String name = portalRuntimePermission.getShortName();
081 String servletContextName =
082 portalRuntimePermission.getServletContextName();
083 String subject = portalRuntimePermission.getSubject();
084 String property = portalRuntimePermission.getProperty();
085
086 String key = null;
087 String value = subject;
088
089 if (name.startsWith(PORTAL_RUNTIME_PERMISSION_GET_CLASSLOADER)) {
090 key = "security-manager-class-loader-reference-ids";
091 }
092 else if (name.equals(PORTAL_RUNTIME_PERMISSION_EXPANDO_BRIDGE)) {
093 key = "security-manager-expando-bridge";
094 }
095 else if (name.equals(PORTAL_RUNTIME_PERMISSION_GET_BEAN_PROPERTY)) {
096 StringBundler sb = new StringBundler(4);
097
098 sb.append("security-manager-get-bean-property");
099 sb.append(StringPool.OPEN_BRACKET);
100 sb.append(servletContextName);
101 sb.append(StringPool.CLOSE_BRACKET);
102
103 key = sb.toString();
104
105 if (Validator.isNotNull(property)) {
106 value = value + StringPool.POUND + property;
107 }
108 }
109 else if (name.equals(PORTAL_RUNTIME_PERMISSION_PORTLET_BAG_POOL)) {
110 key = "security-manager-portlet-bag-pool-portlet-ids";
111 }
112 else if (name.equals(PORTAL_RUNTIME_PERMISSION_SEARCH_ENGINE)) {
113 key = "security-manager-search-engine-ids";
114 }
115 else if (name.equals(PORTAL_RUNTIME_PERMISSION_SET_BEAN_PROPERTY)) {
116 StringBundler sb = new StringBundler(4);
117
118 sb.append("security-manager-set-bean-property");
119 sb.append(StringPool.OPEN_BRACKET);
120 sb.append(servletContextName);
121 sb.append(StringPool.CLOSE_BRACKET);
122
123 key = sb.toString();
124
125 if (Validator.isNotNull(property)) {
126 value = value + StringPool.POUND + property;
127 }
128 }
129 else if (name.equals(PORTAL_RUNTIME_PERMISSION_THREAD_POOL_EXECUTOR)) {
130 key = "security-manager-thread-pool-executor-names";
131 }
132 else {
133 return null;
134 }
135
136 AuthorizationProperty authorizationProperty =
137 new AuthorizationProperty();
138
139 authorizationProperty.setKey(key);
140 authorizationProperty.setValue(value);
141
142 return authorizationProperty;
143 }
144
145 public boolean implies(Permission permission) {
146 PortalRuntimePermission portalRuntimePermission =
147 (PortalRuntimePermission)permission;
148
149 String name = portalRuntimePermission.getShortName();
150 String subject = portalRuntimePermission.getSubject();
151 String servletContextName =
152 portalRuntimePermission.getServletContextName();
153 String property = GetterUtil.getString(
154 portalRuntimePermission.getProperty());
155
156 if (name.equals(PORTAL_RUNTIME_PERMISSION_EXPANDO_BRIDGE)) {
157 if (!_expandoBridgeClassNames.contains(subject)) {
158 logSecurityException(
159 _log, "Attempted to get Expando bridge on " + subject);
160
161 return false;
162 }
163 }
164 else if (name.equals(PORTAL_RUNTIME_PERMISSION_GET_BEAN_PROPERTY)) {
165 if (!hasGetBeanProperty(
166 servletContextName, subject, property, permission)) {
167
168 if (Validator.isNotNull(property)) {
169 logSecurityException(
170 _log,
171 "Attempted to get bean property " + property + " on " +
172 subject + " from " + servletContextName);
173 }
174 else {
175 logSecurityException(
176 _log, "Attempted to get bean property on " + subject +
177 " from " + servletContextName);
178 }
179
180 return false;
181 }
182 }
183 else if (name.startsWith(PORTAL_RUNTIME_PERMISSION_GET_CLASSLOADER)) {
184 if (!hasGetClassLoader(subject, permission)) {
185 logSecurityException(
186 _log, "Attempted to get class loader " + subject);
187
188 return false;
189 }
190 }
191 else if (name.equals(PORTAL_RUNTIME_PERMISSION_PORTLET_BAG_POOL)) {
192 if (!hasPortletBagPoolPortletId(subject)) {
193 logSecurityException(
194 _log,
195 "Attempted to handle portlet bag pool portlet ID " +
196 subject);
197
198 return false;
199 }
200 }
201 else if (name.equals(PORTAL_RUNTIME_PERMISSION_SEARCH_ENGINE)) {
202 if (!_searchEngineIds.contains(subject)) {
203 logSecurityException(
204 _log, "Attempted to get search engine " + subject);
205
206 return false;
207 }
208 }
209 else if (name.equals(PORTAL_RUNTIME_PERMISSION_SET_BEAN_PROPERTY)) {
210 if (!hasSetBeanProperty(servletContextName, subject, property)) {
211 if (Validator.isNotNull(property)) {
212 logSecurityException(
213 _log,
214 "Attempted to set bean property " + property + " on " +
215 subject + " from " + servletContextName);
216 }
217 else {
218 logSecurityException(
219 _log, "Attempted to set bean property on " + subject +
220 " from " + servletContextName);
221 }
222
223 return false;
224 }
225 }
226 else if (name.equals(PORTAL_RUNTIME_PERMISSION_THREAD_POOL_EXECUTOR)) {
227 if (!hasThreadPoolExecutorNames(subject)) {
228 logSecurityException(
229 _log,
230 "Attempted to modify thread pool executor " + subject);
231
232 return false;
233 }
234 }
235
236 return true;
237 }
238
239 protected boolean hasGetBeanProperty(
240 String servletContextName, String className, String property,
241 Permission permission) {
242
243 if (servletContextName.equals(getServletContextName())) {
244 return true;
245 }
246
247 int stackIndex = getStackIndex(13, 12);
248
249 Class<?> callerClass = Reflection.getCallerClass(stackIndex);
250
251 if (isTrustedCaller(callerClass, permission)) {
252 stackIndex = stackIndex + 1;
253
254 if (callerClass.equals(BeanLocatorImpl.class)) {
255 stackIndex = stackIndex + 2;
256 }
257
258 callerClass = Reflection.getCallerClass(stackIndex);
259
260 if (!callerClass.equals(TemplateContextHelper.class) &&
261 isTrustedCaller(callerClass, permission)) {
262
263 return true;
264 }
265 }
266
267 Set<String> getBeanPropertyClassNames = _getBeanPropertyClassNames.get(
268 servletContextName);
269
270 if (getBeanPropertyClassNames == null) {
271 return false;
272 }
273
274 if (getBeanPropertyClassNames.contains(className)) {
275 return true;
276 }
277
278 if (Validator.isNotNull(property)) {
279 if (getBeanPropertyClassNames.contains(
280 className.concat(StringPool.POUND).concat(property))) {
281
282 return true;
283 }
284 }
285
286 return false;
287 }
288
289 protected boolean hasGetClassLoader(
290 String classLoaderReferenceId, Permission permission) {
291
292 int stackIndex = getStackIndex(12, 11);
293
294 if (_classLoaderReferenceIds.contains(classLoaderReferenceId)) {
295 return true;
296 }
297
298 Class<?> callerClass = Reflection.getCallerClass(stackIndex);
299
300 String callerClassName = callerClass.getName();
301
302 if (callerClassName.equals(
303 PortalClassLoaderUtil.class.getName()) ||
304 callerClassName.equals(
305 PortletClassLoaderUtil.class.getName())) {
306
307 callerClass = Reflection.getCallerClass(stackIndex + 1);
308 }
309 else if (callerClassName.equals(
310 DynamicQueryFactoryImpl.class.getName())) {
311
312 callerClass = Reflection.getCallerClass(stackIndex + 3);
313 }
314
315 if (isTrustedCaller(callerClass, permission)) {
316 return true;
317 }
318
319 Class<?> superClass = callerClass.getSuperclass();
320
321
322
323 if (Modifier.isAbstract(callerClass.getModifiers()) &&
324 (superClass.equals(BaseLocalServiceImpl.class) ||
325 superClass.equals(BasePersistenceImpl.class) ||
326 superClass.equals(BaseServiceImpl.class))) {
327
328 return true;
329 }
330
331 return false;
332 }
333
334 protected boolean hasPortletBagPoolPortletId(String portletId) {
335 for (Pattern portletBagPoolPortletIdPattern :
336 _portletBagPoolPortletIdPatterns) {
337
338 Matcher matcher = portletBagPoolPortletIdPattern.matcher(portletId);
339
340 if (matcher.matches()) {
341 return true;
342 }
343 }
344
345 return false;
346 }
347
348 protected boolean hasSetBeanProperty(
349 String servletContextName, String className, String property) {
350
351 if (servletContextName.equals(getServletContextName())) {
352 return true;
353 }
354
355 Set<String> setBeanPropertyClassNames = _setBeanPropertyClassNames.get(
356 servletContextName);
357
358 if (setBeanPropertyClassNames == null) {
359 return false;
360 }
361
362 if (setBeanPropertyClassNames.contains(className)) {
363 return true;
364 }
365
366 if (Validator.isNotNull(property)) {
367 if (setBeanPropertyClassNames.contains(
368 className.concat(StringPool.POUND).concat(property))) {
369
370 return true;
371 }
372 }
373
374 return false;
375 }
376
377 protected boolean hasThreadPoolExecutorNames(
378 String threadPoolExecutorName) {
379
380 for (Pattern threadPoolExecutorNamePattern :
381 _threadPoolExecutorNamePatterns) {
382
383 Matcher matcher = threadPoolExecutorNamePattern.matcher(
384 threadPoolExecutorName);
385
386 if (matcher.matches()) {
387 return true;
388 }
389 }
390
391 return false;
392 }
393
394 protected void initClassLoaderReferenceIds() {
395 _classLoaderReferenceIds = getPropertySet(
396 "security-manager-class-loader-reference-ids");
397
398 if (_log.isDebugEnabled()) {
399 Set<String> classLoaderReferenceIds = new TreeSet<String>(
400 _classLoaderReferenceIds);
401
402 for (String classLoaderReferenceId : classLoaderReferenceIds) {
403 _log.debug(
404 "Allowing access to class loader for reference " +
405 classLoaderReferenceId);
406 }
407 }
408 }
409
410 protected void initExpandoBridgeClassNames() {
411 _expandoBridgeClassNames = getPropertySet(
412 "security-manager-expando-bridge");
413
414 if (_log.isDebugEnabled()) {
415 Set<String> classNames = new TreeSet<String>(
416 _expandoBridgeClassNames);
417
418 for (String className : classNames) {
419 _log.debug("Allowing Expando bridge on class " + className);
420 }
421 }
422 }
423
424 protected void initGetBeanPropertyClassNames() {
425 Properties properties = getProperties();
426
427 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
428 String key = (String)entry.getKey();
429 String value = (String)entry.getValue();
430
431 if (!key.startsWith("security-manager-get-bean-property[")) {
432 continue;
433 }
434
435 int x = key.indexOf("[");
436 int y = key.indexOf("]", x);
437
438 String servletContextName = key.substring(x + 1, y);
439
440 Set<String> getBeanPropertyClassNames = SetUtil.fromArray(
441 StringUtil.split(value));
442
443 _getBeanPropertyClassNames.put(
444 servletContextName, getBeanPropertyClassNames);
445
446 if (_log.isDebugEnabled() &&
447 !servletContextName.equals(_PORTAL_SERVLET_CONTEXT_NAME)) {
448
449 Set<String> classNames = new TreeSet<String>(
450 getBeanPropertyClassNames);
451
452 for (String className : classNames) {
453 _log.debug(
454 "Allowing get bean property from " +
455 servletContextName + " on class " + className);
456 }
457 }
458 }
459
460
461
462 Set<String> getBeanPropertyClassNames = _getBeanPropertyClassNames.get(
463 _PORTAL_SERVLET_CONTEXT_NAME);
464
465 if (getBeanPropertyClassNames == null) {
466 getBeanPropertyClassNames = getPropertySet(
467 "security-manager-get-bean-property");
468 }
469 else {
470 getBeanPropertyClassNames.addAll(
471 getPropertySet("security-manager-get-bean-property"));
472 }
473
474 _getBeanPropertyClassNames.put(
475 _PORTAL_SERVLET_CONTEXT_NAME, getBeanPropertyClassNames);
476
477 if (_log.isDebugEnabled()) {
478 Set<String> classNames = new TreeSet<String>(
479 getBeanPropertyClassNames);
480
481 for (String className : classNames) {
482 _log.debug(
483 "Allowing get bean property from " +
484 _PORTAL_SERVLET_CONTEXT_NAME + " on class " +
485 className);
486 }
487 }
488 }
489
490 protected void initPortletBagPoolPortletIds() {
491 Set<String> portletBagPoolPortletIds = getPropertySet(
492 "security-manager-portlet-bag-pool-portlet-ids");
493
494 _portletBagPoolPortletIdPatterns = new ArrayList<Pattern>(
495 portletBagPoolPortletIds.size());
496
497 for (String portletBagPoolPortletId : portletBagPoolPortletIds) {
498 Pattern portletBagPoolPortletIdPattern = Pattern.compile(
499 portletBagPoolPortletId);
500
501 _portletBagPoolPortletIdPatterns.add(
502 portletBagPoolPortletIdPattern);
503
504 if (_log.isDebugEnabled()) {
505 _log.debug(
506 "Allowing portlet bag pool portlet IDs that match the " +
507 "regular expression " + portletBagPoolPortletId);
508 }
509 }
510 }
511
512 protected void initSearchEngineIds() {
513 _searchEngineIds = getPropertySet("security-manager-search-engine-ids");
514
515 if (_log.isDebugEnabled()) {
516 Set<String> searchEngineIds = new TreeSet<String>(_searchEngineIds);
517
518 for (String searchEngineId : searchEngineIds) {
519 _log.debug("Allowing search engine " + searchEngineId);
520 }
521 }
522 }
523
524 protected void initSetBeanPropertyClassNames() {
525 Properties properties = getProperties();
526
527 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
528 String key = (String)entry.getKey();
529 String value = (String)entry.getValue();
530
531 if (!key.startsWith("security-manager-set-bean-property[")) {
532 continue;
533 }
534
535 int x = key.indexOf("[");
536 int y = key.indexOf("]", x);
537
538 String servletContextName = key.substring(x + 1, y);
539
540 Set<String> setBeanPropertyClassNames = SetUtil.fromArray(
541 StringUtil.split(value));
542
543 _setBeanPropertyClassNames.put(
544 servletContextName, setBeanPropertyClassNames);
545
546 if (_log.isDebugEnabled() &&
547 !servletContextName.equals(_PORTAL_SERVLET_CONTEXT_NAME)) {
548
549 Set<String> classNames = new TreeSet<String>(
550 setBeanPropertyClassNames);
551
552 for (String className : classNames) {
553 _log.debug(
554 "Allowing set bean property from " +
555 servletContextName + " on class " + className);
556 }
557 }
558 }
559
560
561
562 Set<String> setBeanPropertyClassNames = _setBeanPropertyClassNames.get(
563 _PORTAL_SERVLET_CONTEXT_NAME);
564
565 if (setBeanPropertyClassNames == null) {
566 setBeanPropertyClassNames = getPropertySet(
567 "security-manager-set-bean-property");
568 }
569 else {
570 setBeanPropertyClassNames.addAll(
571 getPropertySet("security-manager-set-bean-property"));
572 }
573
574 _setBeanPropertyClassNames.put(
575 _PORTAL_SERVLET_CONTEXT_NAME, setBeanPropertyClassNames);
576
577 if (_log.isDebugEnabled()) {
578 Set<String> classNames = new TreeSet<String>(
579 setBeanPropertyClassNames);
580
581 for (String className : classNames) {
582 _log.debug(
583 "Allowing set bean property from " +
584 _PORTAL_SERVLET_CONTEXT_NAME + " on class " +
585 className);
586 }
587 }
588 }
589
590 protected void initThreadPoolExecutorNames() {
591 Set<String> threadPoolExecutorNames = getPropertySet(
592 "security-manager-thread-pool-executor-names");
593
594 _threadPoolExecutorNamePatterns = new ArrayList<Pattern>(
595 threadPoolExecutorNames.size());
596
597 for (String threadPoolExecutorName : threadPoolExecutorNames) {
598 Pattern threadPoolExecutorNamePattern = Pattern.compile(
599 threadPoolExecutorName);
600
601 _threadPoolExecutorNamePatterns.add(threadPoolExecutorNamePattern);
602
603 if (_log.isDebugEnabled()) {
604 _log.debug(
605 "Allowing thread pool executors that match the regular " +
606 "expression " + threadPoolExecutorName);
607 }
608 }
609 }
610
611 private static final String _PORTAL_SERVLET_CONTEXT_NAME = "portal";
612
613 private static Log _log = LogFactoryUtil.getLog(PortalRuntimeChecker.class);
614
615 private Set<String> _classLoaderReferenceIds;
616 private Set<String> _expandoBridgeClassNames;
617 private Map<String, Set<String>> _getBeanPropertyClassNames =
618 new HashMap<String, Set<String>>();
619 private List<Pattern> _portletBagPoolPortletIdPatterns;
620 private Set<String> _searchEngineIds;
621 private Map<String, Set<String>> _setBeanPropertyClassNames =
622 new HashMap<String, Set<String>>();
623 private List<Pattern> _threadPoolExecutorNamePatterns;
624
625 }