I thought it would be helpful to start this issue on the users list because it will contain a lot of helpful search terms.

I am upgrading a stable production tomcat 7.0.52 system to tomcat 8.5.54. Both were built from source code (tc8 cloned from git) and compiled under openjdk8.

Many users have pre-hashed SHA-1 passwords stored in the LDAP directory. My SSO login jsp uses Form authentication. Because the only Connector services https on port 443, there is no security exposure from sending the user-entered cleartext password "over the wire" to tomcat.

The working tomcat 7 Engine has the following Realm definition:-

      <Realm className="org.apache.catalina.realm.LockOutRealm"
             cacheSize="1000"
             failureCount="4"
             lockOutTime="1200"
             cacheRemovalWarningTime="86400" >

<!-- This is the real Realm, which uses our apacheds LDAP directory. User roles are defined as user attributes, not as group membership.
            -->
          <Realm className="org.apache.catalina.realm.JNDIRealm"
connectionName="uid=tomcatAuthenticate,ou=Special Users,o=pingtoo.com"
                 connectionPassword="<redacted>"
                 connectionURL="ldap://ldap.pingtoo.com:10389";
                 userBase="ou=people,o=pingtoo.com"
                 userSubtree="false"
                 userSearch="(uid={0})"
                 userRoleName="tomcatRole"
                 userPassword="userPassword"
                 digest="SHA" />
      </Realm>

... and the Host has the following:-

<Valve className="org.apache.catalina.valves.ExtendedAccessLogValve"
               directory="logs"
               prefix="access." suffix=".txt"
pattern="c-ip x-H(authType) x-H(remoteUser) date time cs-method cs-uri sc-status bytes"
               resolveHosts="false"/>

        <!-- activate single signon so a user only needs to authenticate
             once to have access to all applications on this virtual host
             under the same set of credentials. -->
        <Valve className="org.apache.catalina.authenticator.SingleSignOn"/>

As I said earlier, but without sounding like I am complaining, this environment worked as intended (by me, the webadmin) with tomcat7.0.52.

I am aware of the following information on the tomcat 8 Documentation:-

https://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html

and

https://tomcat.apache.org/tomcat-8.0-doc/config/realm.html#JNDI_Directory_Realm_-_org.apache.catalina.realm.JNDIRealm

It states the "algorithm attribute is deprecated. Set the algorithm on a nested CredentialHandler element instead."

I deleted the algorithm attribute from the tomcat8 JNDIRealm definition, and nested three different versions for different test runs:-

<CredentialHandler className="org.apache.catalina.realm.MessageDigestCredentialHandler"
                           algorithm="SHA" />

It made no difference whether I used "SHA-1" or "SHA-256".

Under a netbeans remote debugging session with the tomcat8 server, I initially set a breakpoint in FormAuthenticator.doAuthenticate, then instruction stepped to drill down into the JNDI authentication logic.

JNDIREalm.compareCredentials executes:-

return getCredentialHandler().matches(credentials, password);

.. where the calling parameters are the {SHA}<base64> password from the LDAP directory and the cleartext password string from the logon Form.

MessageDigestCredentialHandler.matches immediately calls its own getAlgorithm method, which returns null. Without a correct digest handler, the two password parameters are compared as simple strings. This means the authentication fails!

To prove my point, I pasted the hashed copy of the LDAP userpassword (as plain text) into the FormAuthenticator field. Naturally, without a valid hash algorithm the authentication is successful simply because the two strings match exactly.

I searched for usages of MessageDigestCredentialHandler.setAlgorithm, but only found it used once - within TestJNDIRealm. I did not find any occurrences within tomcat mainline code, but would not be surprised if the algorithm was intended to be set within code which used introspection at runtime.

My initial code inspection makes me strongly suspect tomcat does not initialise JNDIRealm and a nested CredentialHandler properly during startup. However, I am not smart enough to attach my debugger to the tomcat jvm until it is too late.

I had a smart idea... at a breakpoint I changed the value of the algorithm instance variable from null to "SHA" before the comparison, but I was slapped down with the following Exception:-

BB 2020-04-14T15:22:44,257 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[www2.pingtoo.com]] Exception Processing /staticPingToo/restricted/j_security_check
java.lang.IllegalStateException: Must call init() first
at org.apache.tomcat.util.security.ConcurrentMessageDigest.digest(ConcurrentMessageDigest.java:71) ~[tomcat-util.jar:8.5.54] at org.apache.tomcat.util.security.ConcurrentMessageDigest.digest(ConcurrentMessageDigest.java:63) ~[tomcat-util.jar:8.5.54] at org.apache.catalina.realm.MessageDigestCredentialHandler.matches(MessageDigestCredentialHandler.java:114) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.JNDIRealm.compareCredentials(JNDIRealm.java:1822) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.JNDIRealm.checkCredentials(JNDIRealm.java:1782) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1427) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.JNDIRealm.authenticate(JNDIRealm.java:1304) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.CombinedRealm.authenticate(CombinedRealm.java:197) ~[catalina.jar:8.5.54] at org.apache.catalina.realm.LockOutRealm.authenticate(LockOutRealm.java:159) ~[catalina.jar:8.5.54] at org.apache.catalina.authenticator.FormAuthenticator.doAuthenticate(FormAuthenticator.java:243) ~[catalina.jar:8.5.54] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:633) ~[catalina.jar:8.5.54] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:8.5.54] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [catalina.jar:8.5.54] at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:240) [catalina.jar:8.5.54] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) [catalina.jar:8.5.54] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [catalina.jar:8.5.54] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:8.5.54] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:615) [tomcat-coyote.jar:8.5.54] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-coyote.jar:8.5.54] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818) [tomcat-coyote.jar:8.5.54] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1627) [tomcat-coyote.jar:8.5.54] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:8.5.54] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_242] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_242] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.5.54]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_242]

Obviously, my smart idea wasn't smart enough!!!

So, if anyone has read this far, perhaps you can suggest my next best course of action. Does this seem to be a bug in tomcat processing of server.xml and initialisation of the JNDIReal nested CredentialHandler's algorithm attribute? Is there a smart way to catch the tc8 startup process and catch it early enough in my remote debugger?

Are the classes org.apache.catalina.storeconfig.RealmSF and CredentialHandlerSF where I should be looking for a bug? Or perhaps I have just coded my server.xml badly and the algorithm is being silently ignored?

Hopefully...

Brian


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to