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