[jcifs] More classloader issues

Michael B. Allen miallen at eskimo.com
Sat Dec 14 07:37:57 EST 2002

On Fri, 13 Dec 2002 10:34:17 -0500
"Glass, Eric" <eric.glass at capitalone.com> wrote:

> > 	The "only way"? Putting it in the container's CLASSPATH 
> > works. At least with
> > 	Resin it does. I have Resin running NetworkExplorer 
> > servlet at work which uses
> > 	the NtlmHttpFilter and the NtlmPasswordAuthentication 
> > object returned by it to
> > 	access SmbFile resources. I put the jar in the 
> > top-level resin lib directory.
> > 
> Sorry -- I should have further qualified this statement as "under Tomcat".
> The issue stems from how the different servlet containers implement web
> application classloaders.
> Attached is a servlet which prints out some information regarding the
> classloaders used for some key classes.  Also attached is the output when
> run under Tomcat and Resin.
> Resin appends necessary support classes (core servlet classes, etc.) to the
> system classpath; it then creates a child classloader for each web
> application (this is necessary under any servlet container, to prevent the
> occurrence that 2 web applications have identically-named classes).
> Tomcat operates a bit differently -- rather than appending to the system
> classpath, it creates a classloader for the "common" libraries (including
> the core servlet library) which has the system classloader as its parent.
> It then creates a "shared" classloader as a child of the common classloader
> (for shared libraries not required by the container itself), and a
> classloader for each web application, which has the shared classloader as
> its parent.
> So the Tomcat classloader hierarchy looks like this (from
> http://jakarta.apache.org/tomcat/tomcat-4.1-doc/class-loader-howto.html):
>       Bootstrap
>           |
>        System
>           |
>        Common
>       /      \
>  Catalina   Shared
>              /   \
>         Webapp1  Webapp2 ...
> Neither implementation is wrong; just different.  But the implication of
> Tomcat's approach is that classes loaded by the system classloader (or the
> boot classloader, which is where URL is actually loaded) cannot see classes
> from the common classloader -- including javax.servlet.Filter, etc..

I don't understand. If Webapp1 tries to access an SmbFile that will
execute the static initializer in jcifs.Config which will add 'jcifs' to
the java.protocol.handler.pkgs System property. The jCIFS classes should
be available to the System class loader (that's our requirement). When
the SmbFile constructor is called, the URL class (loaded from the
System class loader) will see 'smb://' and try to find a handler. From
the code you show below it should find jcifs.smb.Handler in the System
class loader. Everything ok so far right?

> Ordinarily, this would not be an issue.  But in order for URL to properly
> load the SMB URL handler, it must be installed under the system (or boot)
> classloader.

> Upon further investigation, this is why URL does:
> Class cls = null;
> try {
>     cls = Class.forName(clsName);
> } catch (ClassNotFoundException e) {
>     ClassLoader cl = ClassLoader.getSystemClassLoader();
>     if (cl != null) {
>         cls = cl.loadClass(clsName);
>     }
> }
> The first attempt will load classes from the BOOT classloader (which is the
> defining classloader for the URL class).  The second will load from the
> SYSTEM classloader.  This is also why my previous detection code did not
> work; I was trying to load the SMB handler directly from URL's classloader
> (the boot classpath) rather than the system classloader (which is where it
> will be).
> The problem will arise under Tomcat when NtlmFilter is loaded; it will find
> the implementation under the system classloader, but NtlmFilter will be
> unable to find javax.servlet.Filter (which is loaded under the common
> classloader).  This will cause a NoClassDefFoundError, specifically:

But the container will be trying to load classes in the Webapp class
loader. So when accessing jcifs.http.NtlmHttpFilter it will fail as it
will in the Shared and Common class loaders before successfully loading
from the System class loader. Now when resolving the javax.servlet.Filter
baseclass and other servlet classes will it try from the Webapp
classloader again or degenerate to the classloader from which it just
loaded jcifs.http.NtlmHttpFilter? I don't see the security risk in
starting from the original context loader again.

Also getting jcifs.smb.Handler installed in the System class loader is
a completely separate issue right? There's really no relation between
the problems other than their both being "class loading" issues.

> java.lang.NoClassDefFoundError: javax/servlet/Filter
> 	at java.lang.ClassLoader.defineClass0(Native Method)
> 	at java.lang.ClassLoader.defineClass(ClassLoader.java:509)
> 	at
> java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
> 	at java.net.URLClassLoader.defineClass(URLClassLoader.java:246)
> 	at java.net.URLClassLoader.access$100(URLClassLoader.java:54)
> 	at java.net.URLClassLoader$1.run(URLClassLoader.java:193)
> 	at java.security.AccessController.doPrivileged(Native Method)
> 	at java.net.URLClassLoader.findClass(URLClassLoader.java:186)
> 	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
> 	at java.lang.ClassLoader.loadClass(ClassLoader.java:299)
> 	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:265)
> 	at java.lang.ClassLoader.loadClass(ClassLoader.java:262)
> 	at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1340)
> 	at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1274)
> 	at
> Tomcat users will experience similar errors whenever classes loaded under
> the system classloader attempt to access classes available only to the
> common classloader.
> A workaround for Tomcat users (short of splitting jCIFS into two jars) would
> be to move servlet.jar (the core servlet classes) from
> $TOMCAT_HOME/common/lib into the jre/lib/ext directory (or make it otherwise
> available to the system classloader).
> > >  one contains
> > > the core jCIFS classes (and should be installed as an 
> > extension or otherwise
> > > under the system classloader).  The other contains the http 
> > stuff, and
> > > should be installed under the web application classloader (i.e., in
> > > WEB-INF/lib).
> > > 
> > 	I don't want to break up the package unnecessarily. If 
> > someone has permission
> > 	to add the jar to the jre/lib/ext directory that 
> > implies they have permission to add
> > 	it to the container CLASSPATH.
> > 
> > 	So the solution to both the class loader and security 
> > policy issues is to just make
> > 	sure that the jCIFS jar is in the container's 
> > CLASSPATH. Presumably these
> > 	security policy files are associated with files loaded 
> > by web apps rather than the
> > 	container itself. Right?
> > 
> > 
> The security policy would be applied and enforced by the servlet container;
> the settings would apply to the entire JVM (although typically it would
> grant AllPermission to the container codebase, so your statement would
> generally be correct in that this would solve the classloader issues related
> to SMB URLs).  The location/filename of the policy file would be
> container-specific (as would the means of telling the container to apply the
> policy); Tomcat uses "catalina.policy" by default (in the "conf"
> subdirectory).  When enforced by launching Tomcat with the "-security" flag,
> this provides a level of protection against malicious servlets by installing
> a security manager which enforces the policy (via
> System.setSecurityManager).  The person responsible for maintaining the
> servlet container would edit this file and add the line:
>     permission java.util.PropertyPermission "java.protocol.handler.pkgs",

Ok, this is good info but I don't think it will ever be necessary. Our
requirement is that the jCIFS jar be available to the System
classloader. This basically equites to putting it in the containers top
level lib directory and possibly adding it explicitly to the container
CLASSPATH. Whatever the case, the code will likely have AllPermission
or similar. Or it is possible (although less likely) that certain
directories like all WEB-INF/{lib,classes} directories have their own
associated more restricted permissions.

I just need to establish the simplest "correct" installation procedure. So
far, ensuring the jar is loaded by the System class loader and that it's
codeBase has the necesary PropertyPermission should work. If anyone wants
a more finely grained procedure they can be referred to these messages
in the archive.

The only remaining issue is making servlet classes available to the
NtlmHttpFilter which is loaded in the System class loader but I'm not
sure I understand exactly why that's a problem.

A  program should be written to model the concepts of the task it
performs rather than the physical world or a process because this
maximizes  the  potential  for it to be applied to tasks that are
conceptually  similar and, more important, to tasks that have not
yet been conceived. 

More information about the jcifs mailing list