From 655be64a1753a3077a81fc0c34573bca74dcf5a0 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 25 Jan 2018 12:21:08 -0500 Subject: [PATCH] [JENKINS-49147] Tolerate unusual CodeSource.location format from old versions of Tomcat. --- .../jenkins/security/ClassFilterImpl.java | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/jenkins/security/ClassFilterImpl.java b/core/src/main/java/jenkins/security/ClassFilterImpl.java index 2de71b01e334..0c554232b48f 100644 --- a/core/src/main/java/jenkins/security/ClassFilterImpl.java +++ b/core/src/main/java/jenkins/security/ClassFilterImpl.java @@ -28,6 +28,7 @@ import hudson.ExtensionList; import hudson.Main; import hudson.remoting.ClassFilter; +import hudson.remoting.Which; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -49,6 +50,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import jenkins.model.Jenkins; import jenkins.util.SystemProperties; import org.apache.commons.io.IOUtils; @@ -71,11 +74,14 @@ public class ClassFilterImpl extends ClassFilter { private static /* not final */ boolean SUPPRESS_WHITELIST = SystemProperties.getBoolean("jenkins.security.ClassFilterImpl.SUPPRESS_WHITELIST"); private static /* not final */ boolean SUPPRESS_ALL = SystemProperties.getBoolean("jenkins.security.ClassFilterImpl.SUPPRESS_ALL"); + private static final String JENKINS_LOC = codeSource(Jenkins.class); + private static final String REMOTING_LOC = codeSource(ClassFilter.class); + /** * Register this implementation as the default in the system. */ public static void register() { - if (Main.isUnitTest && Jenkins.class.getProtectionDomain().getCodeSource().getLocation() == null) { + if (Main.isUnitTest && JENKINS_LOC == null) { mockOff(); return; } @@ -146,10 +152,9 @@ public boolean isBlacklisted(Class _c) { LOGGER.log(Level.FINE, "permitting {0} since it is an enum", name); return false; } - CodeSource codeSource = c.getProtectionDomain().getCodeSource(); - URL location = codeSource != null ? codeSource.getLocation() : null; + String location = codeSource(c); if (location != null) { - if (isLocationWhitelisted(location.toString())) { + if (isLocationWhitelisted(location)) { LOGGER.log(Level.FINE, "permitting {0} due to its location in {1}", new Object[] {name, location}); return false; } @@ -176,11 +181,11 @@ public boolean isBlacklisted(Class _c) { private static final Pattern CLASSES_JAR = Pattern.compile("(file:/.+/)WEB-INF/lib/classes[.]jar"); private boolean isLocationWhitelisted(String _loc) { return codeSourceCache.computeIfAbsent(_loc, loc -> { - if (loc.equals(Jenkins.class.getProtectionDomain().getCodeSource().getLocation().toString())) { + if (loc.equals(JENKINS_LOC)) { LOGGER.log(Level.FINE, "{0} seems to be the location of Jenkins core, OK", loc); return true; } - if (loc.equals(ClassFilter.class.getProtectionDomain().getCodeSource().getLocation().toString())) { + if (loc.equals(REMOTING_LOC)) { LOGGER.log(Level.FINE, "{0} seems to be the location of Remoting, OK", loc); return true; } @@ -241,6 +246,34 @@ private boolean isLocationWhitelisted(String _loc) { }); } + /** + * Tries to determine what JAR file a given class was loaded from. + * The location is an opaque string suitable only for comparison to others. + * Similar to {@link Which#jarFile(Class)} but potentially faster, and more tolerant of unknown URL formats. + * @param c some class + * @return something typically like {@code file:/…/plugins/structs/WEB-INF/lib/structs-1.10.jar}; + * or null for classes in the Java Platform, some generated classes, etc. + */ + private static @CheckForNull String codeSource(@Nonnull Class c) { + CodeSource cs = c.getProtectionDomain().getCodeSource(); + if (cs == null) { + return null; + } + URL loc = cs.getLocation(); + if (loc == null) { + return null; + } + String r = loc.toString(); + if (r.endsWith(".class")) { + // JENKINS-49147: Tomcat bug. Now do the more expensive check… + String suffix = c.getName().replace('.', '/') + ".class"; + if (r.endsWith(suffix)) { + r = r.substring(0, r.length() - suffix.length()); + } + } + return r; + } + private static boolean isPluginManifest(Manifest mf) { Attributes attr = mf.getMainAttributes(); return attr.getValue("Short-Name") != null && (attr.getValue("Plugin-Version") != null || attr.getValue("Jenkins-Version") != null) ||