001
014
015 package com.liferay.portal.security.pacl.checker;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.messaging.BaseAsyncDestination;
020 import com.liferay.portal.kernel.servlet.PortalClassLoaderFilter;
021 import com.liferay.portal.kernel.servlet.PortalClassLoaderServlet;
022 import com.liferay.portal.kernel.util.JavaDetector;
023 import com.liferay.portal.kernel.util.PathUtil;
024 import com.liferay.portal.kernel.util.ServerDetector;
025 import com.liferay.portal.kernel.util.StringPool;
026 import com.liferay.portal.kernel.util.StringUtil;
027 import com.liferay.portal.security.lang.PortalSecurityManagerThreadLocal;
028 import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
029 import com.liferay.portal.security.pacl.PACLClassUtil;
030
031 import java.security.Permission;
032
033 import java.util.Set;
034 import java.util.TreeSet;
035
036 import org.apache.xerces.impl.dv.DatatypeException;
037 import org.apache.xerces.parsers.AbstractDOMParser;
038
039 import org.springframework.beans.CachedIntrospectionResults;
040 import org.springframework.context.support.AbstractApplicationContext;
041 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
042 import org.springframework.util.ClassUtils;
043
044 import sun.reflect.Reflection;
045
046
050 public class RuntimeChecker extends BaseReflectChecker {
051
052 public void afterPropertiesSet() {
053 initClassLoaderReferenceIds();
054 }
055
056 public void checkPermission(Permission permission) {
057 String name = permission.getName();
058
059 if (name.startsWith(RUNTIME_PERMISSION_ACCESS_CLASS_IN_PACKAGE)) {
060 int pos = name.indexOf(StringPool.PERIOD);
061
062 String pkg = name.substring(pos + 1);
063
064 if (!hasAccessClassInPackage(pkg)) {
065 throwSecurityException(
066 _log, "Attempted to access package " + pkg);
067 }
068 }
069 else if (name.equals(RUNTIME_PERMISSION_ACCESS_DECLARED_MEMBERS)) {
070 if (!hasReflect(permission.getName(), permission.getActions())) {
071 throwSecurityException(
072 _log, "Attempted to access declared members");
073 }
074 }
075 else if (name.equals(RUNTIME_PERMISSION_CREATE_CLASS_LOADER)) {
076 if (PortalSecurityManagerThreadLocal.isCheckCreateClassLoader() &&
077 !isJSPCompiler(permission.getName(), permission.getActions()) &&
078 !hasCreateClassLoader()) {
079
080 throwSecurityException(
081 _log, "Attempted to create a class loader");
082 }
083 }
084 else if (name.startsWith(RUNTIME_PERMISSION_GET_CLASSLOADER)) {
085 if (PortalSecurityManagerThreadLocal.isCheckGetClassLoader() &&
086 !isJSPCompiler(permission.getName(), permission.getActions()) &&
087 !hasGetClassLoader(name)) {
088
089 throwSecurityException(_log, "Attempted to get class loader");
090 }
091 }
092 else if (name.startsWith(RUNTIME_PERMISSION_GET_PROTECTION_DOMAIN)) {
093 if (!hasGetProtectionDomain()) {
094 throwSecurityException(
095 _log, "Attempted to get protection domain");
096 }
097 }
098 else if (name.startsWith(RUNTIME_PERMISSION_GET_ENV)) {
099 int pos = name.indexOf(StringPool.PERIOD);
100
101 String envName = name.substring(pos + 1);
102
103 if (!hasGetEnv(envName)) {
104 throwSecurityException(
105 _log, "Attempted to get environment name " + envName);
106 }
107 }
108 else if (name.equals(RUNTIME_PERMISSION_READ_FILE_DESCRIPTOR)) {
109 if (PortalSecurityManagerThreadLocal.isCheckReadFileDescriptor() &&
110 !hasReadFileDescriptor()) {
111
112 throwSecurityException(
113 _log, "Attempted to read file descriptor");
114 }
115 }
116 else if (name.equals(RUNTIME_PERMISSION_SET_CONTEXT_CLASS_LOADER)) {
117 }
118 else if (name.equals(RUNTIME_PERMISSION_SET_SECURITY_MANAGER)) {
119 throwSecurityException(
120 _log, "Attempted to set another security manager");
121 }
122 else if (name.equals(RUNTIME_PERMISSION_WRITE_FILE_DESCRIPTOR)) {
123 if (PortalSecurityManagerThreadLocal.isCheckWriteFileDescriptor() &&
124 !hasWriteFileDescriptor()) {
125
126 throwSecurityException(
127 _log, "Attempted to write file descriptor");
128 }
129 }
130 else {
131 if (_log.isDebugEnabled()) {
132 Thread.dumpStack();
133 }
134
135 throwSecurityException(
136 _log,
137 "Attempted to " + permission.getName() + " on " +
138 permission.getActions());
139 }
140 }
141
142 protected boolean hasAccessClassInPackage(String pkg) {
143
144
145
146 if (pkg.startsWith("sun.reflect")) {
147 }
148
149 return true;
150 }
151
152 protected boolean hasCreateClassLoader() {
153 if (JavaDetector.isIBM()) {
154 Class<?> callerClass9 = Reflection.getCallerClass(9);
155
156 String callerClassName9 = callerClass9.getName();
157
158 if (callerClassName9.startsWith(_CLASS_NAME_CLASS_DEFINER) &&
159 CheckerUtil.isAccessControllerDoPrivileged(10)) {
160
161 logCreateClassLoader(callerClass9, 9);
162
163 return true;
164 }
165
166 Class<?> callerClass10 = Reflection.getCallerClass(10);
167
168 String callerClassName10 = callerClass10.getName();
169
170 if (callerClassName10.startsWith(_CLASS_NAME_CLASS_DEFINER) &&
171 CheckerUtil.isAccessControllerDoPrivileged(11)) {
172
173 logCreateClassLoader(callerClass10, 10);
174
175 return true;
176 }
177 }
178 else if (JavaDetector.isJDK7()) {
179 Class<?> callerClass11 = Reflection.getCallerClass(11);
180
181 String callerClassName11 = callerClass11.getName();
182
183 if (callerClassName11.startsWith(_CLASS_NAME_CLASS_DEFINER) &&
184 CheckerUtil.isAccessControllerDoPrivileged(12)) {
185
186 logCreateClassLoader(callerClass11, 11);
187
188 return true;
189 }
190 }
191 else {
192 Class<?> callerClass10 = Reflection.getCallerClass(10);
193
194 String callerClassName10 = callerClass10.getName();
195
196 if (callerClassName10.startsWith(_CLASS_NAME_CLASS_DEFINER) &&
197 CheckerUtil.isAccessControllerDoPrivileged(11)) {
198
199 logCreateClassLoader(callerClass10, 10);
200
201 return true;
202 }
203 }
204
205 return false;
206 }
207
208 protected boolean hasGetClassLoader(String name) {
209 int pos = name.indexOf(StringPool.PERIOD);
210
211 if (pos != -1) {
212 String referenceId = name.substring(pos + 1);
213
214 if (_classLoaderReferenceIds.contains(referenceId)) {
215 return true;
216 }
217
218 if (referenceId.equals("portal")) {
219 Class<?> callerClass7 = Reflection.getCallerClass(7);
220
221 if ((callerClass7 == BaseAsyncDestination.class) ||
222 (callerClass7 == PortalClassLoaderFilter.class) ||
223 (callerClass7 == PortalClassLoaderServlet.class)) {
224
225 return true;
226 }
227 }
228
229 return false;
230 }
231
232 Class<?> callerClass6 = Reflection.getCallerClass(6);
233 Class<?> callerClass7 = Reflection.getCallerClass(7);
234
235 if (_log.isDebugEnabled()) {
236 _log.debug(
237 callerClass7.getName() +
238 " is attempting to get the class loader via " +
239 callerClass6.getName());
240 }
241
242 if ((callerClass7 == CachedIntrospectionResults.class) ||
243 (callerClass7 == ClassUtils.class) ||
244 (callerClass7.getEnclosingClass() ==
245 LocalVariableTableParameterNameDiscoverer.class)) {
246
247 logGetClassLoader(callerClass7, 7);
248
249 return true;
250 }
251
252 if (callerClass6 == Class.class) {
253 if (isJBossMessages(callerClass7) ||
254 isJBossServiceControllerImpl(callerClass7) ||
255 isJOnASModuleImpl(callerClass7) ||
256 isTomcatJdbcLeakPrevention(callerClass7)) {
257
258 logGetClassLoader(callerClass7, 7);
259
260 return true;
261 }
262
263 if (isWebSphereProtectionClassLoader(
264 callerClass7.getEnclosingClass()) &&
265 CheckerUtil.isAccessControllerDoPrivileged(8)) {
266
267 logGetClassLoader(callerClass7, 7);
268
269 return true;
270 }
271 }
272 else if (callerClass6 == ClassLoader.class) {
273 Class<?> callerClass8 = Reflection.getCallerClass(8);
274
275 if (isGlassfishAPIClassLoaderServiceImpl(
276 callerClass8.getEnclosingClass()) &&
277 CheckerUtil.isAccessControllerDoPrivileged(9)) {
278
279 logGetClassLoader(callerClass8, 8);
280
281 return true;
282 }
283
284 if (isResinEnvironmentLocal(callerClass7)) {
285 logGetClassLoader(callerClass7, 7);
286
287 return true;
288 }
289
290 if (isWebLogicGenericClassLoader(
291 callerClass7.getEnclosingClass()) &&
292 CheckerUtil.isAccessControllerDoPrivileged(8)) {
293
294 logGetClassLoader(callerClass7, 7);
295
296 return true;
297 }
298
299 if (isXercesSecuritySupport(callerClass7) &&
300 CheckerUtil.isAccessControllerDoPrivileged(8)) {
301
302 logGetClassLoader(callerClass8, 8);
303
304 return true;
305 }
306
307 Thread currentThread = Thread.currentThread();
308
309 StackTraceElement[] stackTraceElements =
310 currentThread.getStackTrace();
311
312 StackTraceElement stackTraceElement = null;
313
314 if (JavaDetector.isIBM()) {
315 stackTraceElement = stackTraceElements[7];
316 }
317 else {
318 stackTraceElement = stackTraceElements[6];
319 }
320
321 String methodName = stackTraceElement.getMethodName();
322
323 if (methodName.equals(_METHOD_NAME_GET_SYSTEM_CLASS_LOADER)) {
324 if (_log.isInfoEnabled()) {
325 _log.info(
326 "Allowing " + callerClass7.getName() +
327 " to get the system class loader");
328 }
329
330 return true;
331 }
332 }
333 else if (callerClass6 == Thread.class) {
334 boolean allow = false;
335
336 ClassLoader contextClassLoader =
337 PACLClassLoaderUtil.getContextClassLoader();
338 ClassLoader portalClassLoader = getPortalClassLoader();
339
340 if (contextClassLoader == portalClassLoader) {
341 if (PACLClassLoaderUtil.getClassLoader(callerClass7) !=
342 getClassLoader()) {
343
344 allow = true;
345 }
346 }
347 else {
348 allow = true;
349 }
350
351 if (allow) {
352 if (_log.isInfoEnabled()) {
353 _log.info(
354 "Allowing " + callerClass7.getName() +
355 " to access the context class loader");
356 }
357
358 return true;
359 }
360 }
361
362 return false;
363 }
364
365 protected boolean hasGetEnv(String name) {
366 Class<?> callerClass7 = Reflection.getCallerClass(7);
367
368 if (callerClass7 == AbstractApplicationContext.class) {
369 logGetEnv(callerClass7, 7, name);
370
371 return true;
372 }
373
374 if (ServerDetector.isWebSphere()) {
375 if (name.equals("USER_INSTALL_ROOT")) {
376 return true;
377 }
378 }
379
380 return false;
381 }
382
383 protected boolean hasGetProtectionDomain() {
384 Class<?> callerClass8 = Reflection.getCallerClass(8);
385
386 if (isDefaultMBeanServerInterceptor(
387 callerClass8.getEnclosingClass()) &&
388 CheckerUtil.isAccessControllerDoPrivileged(9)) {
389
390 logGetProtectionDomain(callerClass8, 8);
391
392 return true;
393 }
394
395 return false;
396 }
397
398 protected boolean hasReadFileDescriptor() {
399 if (JavaDetector.isJDK7()) {
400 Class<?> callerClass9 = Reflection.getCallerClass(9);
401
402 String callerClassName9 = callerClass9.getName();
403
404 if (callerClassName9.startsWith(_CLASS_NAME_PROCESS_IMPL) &&
405 CheckerUtil.isAccessControllerDoPrivileged(10)) {
406
407 logWriteFileDescriptor(callerClass9, 9);
408
409 return true;
410 }
411 }
412 else {
413 Class<?> callerClass8 = Reflection.getCallerClass(8);
414
415 String callerClassName8 = callerClass8.getName();
416
417 if (callerClassName8.startsWith(_CLASS_NAME_PROCESS_IMPL) &&
418 CheckerUtil.isAccessControllerDoPrivileged(9)) {
419
420 logWriteFileDescriptor(callerClass8, 8);
421
422 return true;
423 }
424 }
425
426 return false;
427 }
428
429 protected boolean hasWriteFileDescriptor() {
430 if (JavaDetector.isJDK7()) {
431 Class<?> callerClass9 = Reflection.getCallerClass(9);
432
433 String callerClassName9 = callerClass9.getName();
434
435 if (callerClassName9.startsWith(_CLASS_NAME_PROCESS_IMPL) &&
436 CheckerUtil.isAccessControllerDoPrivileged(10)) {
437
438 logWriteFileDescriptor(callerClass9, 9);
439
440 return true;
441 }
442 }
443 else {
444 Class<?> callerClass8 = Reflection.getCallerClass(8);
445
446 String callerClassName8 = callerClass8.getName();
447
448 if (callerClassName8.startsWith(_CLASS_NAME_PROCESS_IMPL) &&
449 CheckerUtil.isAccessControllerDoPrivileged(9)) {
450
451 logWriteFileDescriptor(callerClass8, 8);
452
453 return true;
454 }
455 }
456
457 return false;
458 }
459
460 protected void initClassLoaderReferenceIds() {
461 _classLoaderReferenceIds = getPropertySet(
462 "security-manager-class-loader-reference-ids");
463
464 if (_log.isDebugEnabled()) {
465 Set<String> referenceIds = new TreeSet<String>(
466 _classLoaderReferenceIds);
467
468 for (String referenceId : referenceIds) {
469 _log.debug(
470 "Allowing access to class loader for reference " +
471 referenceId);
472 }
473 }
474 }
475
476 protected boolean isDefaultMBeanServerInterceptor(Class<?> clazz) {
477 String className = clazz.getName();
478
479 if (!className.equals(_CLASS_NAME_DEFAULT_MBEAN_SERVER_INTERCEPTOR)) {
480 return false;
481 }
482
483 String classLocation = PACLClassUtil.getClassLocation(clazz);
484
485 if (classLocation.length() > 0) {
486 return false;
487 }
488
489 return true;
490 }
491
492 protected boolean isGlassfishAPIClassLoaderServiceImpl(Class<?> clazz) {
493 if (!ServerDetector.isGlassfish()) {
494 return false;
495 }
496
497 if (clazz == null) {
498 return false;
499 }
500
501 clazz = clazz.getEnclosingClass();
502
503 if (clazz == null) {
504 return false;
505 }
506
507 String className = clazz.getName();
508
509 if (!className.equals(_CLASS_NAME_API_CLASS_LOADER_SERVICE_IMPL)) {
510 return false;
511 }
512
513 String classLocation = PACLClassUtil.getClassLocation(clazz);
514
515 return classLocation.startsWith("bundle:
516 }
517
518 protected boolean isJBossMessages(Class<?> clazz) {
519 if (!ServerDetector.isJBoss()) {
520 return false;
521 }
522
523 String className = clazz.getName();
524
525 if (!className.equals(_CLASS_NAME_MESSAGES)) {
526 return false;
527 }
528
529 String classLocation = PACLClassUtil.getClassLocation(clazz);
530
531 return classLocation.contains(
532 "/modules/org/jboss/logging/main/jboss-logging-");
533 }
534
535 protected boolean isJBossServiceControllerImpl(Class<?> clazz) {
536 if (!ServerDetector.isJBoss()) {
537 return false;
538 }
539
540 String className = clazz.getName();
541
542 if (!className.equals(_CLASS_NAME_SERVICE_CONTROLLER_IMPL)) {
543 return false;
544 }
545
546 String classLocation = PACLClassUtil.getClassLocation(clazz);
547
548 return classLocation.contains("/modules/org/jboss/msc/main/jboss-msc-");
549 }
550
551 protected boolean isJOnASModuleImpl(Class<?> clazz) {
552 if (!ServerDetector.isJOnAS()) {
553 return false;
554 }
555
556 String className = clazz.getName();
557
558 if (!className.equals(_CLASS_NAME_MODULE_IMPL)) {
559 return false;
560 }
561
562 String classLocation = PACLClassUtil.getClassLocation(clazz);
563
564 return classLocation.contains("/lib/bootstrap/felix-launcher.jar!/");
565 }
566
567 protected boolean isResinEnvironmentLocal(Class<?> clazz) {
568 if (!ServerDetector.isResin()) {
569 return false;
570 }
571
572 String className = clazz.getName();
573
574 if (!className.equals(_CLASS_NAME_ENVIRONMENT_LOCAL)) {
575 return false;
576 }
577
578 String actualClassLocation = PACLClassUtil.getClassLocation(clazz);
579 String expectedClassLocation = PathUtil.toUnixPath(
580 System.getProperty("resin.home") + "/lib/resin.jar!/");
581
582 return actualClassLocation.contains(expectedClassLocation);
583 }
584
585 protected boolean isTomcatJdbcLeakPrevention(Class<?> clazz) {
586 if (!ServerDetector.isTomcat()) {
587 return false;
588 }
589
590 String className = clazz.getName();
591
592 if (!className.equals(_CLASS_NAME_JDBC_LEAK_PREVENTION)) {
593 return false;
594 }
595
596 String actualClassLocation = PACLClassUtil.getClassLocation(clazz);
597
598 String expectedClassLocation = PathUtil.toUnixPath(
599 System.getProperty("catalina.base") + "/lib/catalina.jar!/");
600
601 expectedClassLocation += StringUtil.replace(
602 className, StringPool.PERIOD, StringPool.SLASH);
603 expectedClassLocation += ".class";
604
605 if (_log.isDebugEnabled()) {
606 _log.debug("Actual class location " + actualClassLocation);
607 _log.debug("Expected class location " + expectedClassLocation);
608 }
609
610 return actualClassLocation.endsWith(expectedClassLocation);
611 }
612
613 protected boolean isWebLogicGenericClassLoader(Class<?> clazz) {
614 if (!ServerDetector.isWebLogic()) {
615 return false;
616 }
617
618 if (clazz == null) {
619 return false;
620 }
621
622 String className = clazz.getName();
623
624 if (!className.equals(_CLASS_NAME_GENERIC_CLASS_LOADER)) {
625 return false;
626 }
627
628 String classLocation = PACLClassUtil.getClassLocation(clazz);
629
630 if (classLocation.contains(
631 "/modules/com.bea.core.utils.classloaders_") ||
632 classLocation.contains("/patch_jars/BUG")) {
633
634 return true;
635 }
636
637 return false;
638 }
639
640 protected boolean isWebSphereProtectionClassLoader(Class<?> clazz) {
641 if (!ServerDetector.isWebSphere()) {
642 return false;
643 }
644
645 if (clazz == null) {
646 return false;
647 }
648
649 String className = clazz.getName();
650
651 if (!className.equals(_CLASS_NAME_PROTECTION_CLASS_LOADER)) {
652 return false;
653 }
654
655 String classLocation = PACLClassUtil.getClassLocation(clazz);
656
657 return classLocation.startsWith("bundleresource:
658 }
659
660 protected boolean isXercesSecuritySupport(Class<?> clazz) {
661 String className = clazz.getName();
662
663 if (className.contains(".SecuritySupport$") &&
664 ((clazz.getPackage() == AbstractDOMParser.class.getPackage()) ||
665 (clazz.getPackage() == DatatypeException.class.getPackage()))) {
666
667 return true;
668 }
669
670 return false;
671 }
672
673 protected void logCreateClassLoader(Class<?> callerClass, int frame) {
674 if (_log.isInfoEnabled()) {
675 _log.info(
676 "Allowing frame " + frame + " with caller " + callerClass +
677 " to create a class loader");
678 }
679 }
680
681 protected void logGetClassLoader(Class<?> callerClass, int frame) {
682 if (_log.isInfoEnabled()) {
683 _log.info(
684 "Allowing frame " + frame + " with caller " + callerClass +
685 " to get the class loader");
686 }
687 }
688
689 protected void logGetEnv(Class<?> callerClass, int frame, String name) {
690 if (_log.isInfoEnabled()) {
691 _log.info(
692 "Allowing frame " + frame + " with caller " + callerClass +
693 " to get environment " + name);
694 }
695 }
696
697 protected void logGetProtectionDomain(Class<?> callerClass, int frame) {
698 if (_log.isInfoEnabled()) {
699 _log.info(
700 "Allowing frame " + frame + " with caller " + callerClass +
701 " to get the protection domain");
702 }
703 }
704
705 protected void logReadFileDescriptor(Class<?> callerClass, int frame) {
706 if (_log.isInfoEnabled()) {
707 _log.info(
708 "Allowing frame " + frame + " with caller " + callerClass +
709 " to read a file descriptor");
710 }
711 }
712
713 protected void logWriteFileDescriptor(Class<?> callerClass, int frame) {
714 if (_log.isInfoEnabled()) {
715 _log.info(
716 "Allowing frame " + frame + " with caller " + callerClass +
717 " to write a file descriptor");
718 }
719 }
720
721 private static final String _CLASS_NAME_API_CLASS_LOADER_SERVICE_IMPL =
722 "com.sun.enterprise.v3.server.APIClassLoaderServiceImpl";
723
724 private static final String _CLASS_NAME_CLASS_DEFINER =
725 "sun.reflect.ClassDefiner$";
726
727 private static final String _CLASS_NAME_DEFAULT_MBEAN_SERVER_INTERCEPTOR =
728 "com.sun.jmx.interceptor.DefaultMBeanServerInterceptor";
729
730 private static final String _CLASS_NAME_ENVIRONMENT_LOCAL =
731 "com.caucho.loader.EnvironmentLocal";
732
733 private static final String _CLASS_NAME_GENERIC_CLASS_LOADER =
734 "weblogic.utils.classloaders.GenericClassLoader";
735
736 private static final String _CLASS_NAME_JDBC_LEAK_PREVENTION =
737 "org.apache.catalina.loader.JdbcLeakPrevention";
738
739 private static final String _CLASS_NAME_MESSAGES =
740 "org.jboss.logging.Messages";
741
742 private static final String _CLASS_NAME_MODULE_IMPL =
743 "org.apache.felix.framework.ModuleImpl";
744
745 private static final String _CLASS_NAME_PROCESS_IMPL =
746 "java.lang.ProcessImpl$";
747
748 private static final String _CLASS_NAME_PROTECTION_CLASS_LOADER =
749 "com.ibm.ws.classloader.ProtectionClassLoader";
750
751 private static final String _CLASS_NAME_SERVICE_CONTROLLER_IMPL =
752 "org.jboss.msc.service.ServiceControllerImpl";
753
754 private static final String _METHOD_NAME_GET_SYSTEM_CLASS_LOADER =
755 "getSystemClassLoader";
756
757 private static Log _log = LogFactoryUtil.getLog(RuntimeChecker.class);
758
759 private Set<String> _classLoaderReferenceIds;
760
761 }