Okay, so I checked out Tomcat 8.0.32 from source control. I then
reverted MemoryRealm's authenticate method to how it was in 7.0.26 and
built Tomcat and now my authentication works.  This of course is not a
solution, but it obviates most of my other questions.  I guess the
important question is: how do I set the CredentialHandler on the
MemoryRealm?

===================================================================
--- MemoryRealm.java (revision 1734183)
+++ MemoryRealm.java (working copy)
@@ -115,16 +115,16 @@

         GenericPrincipal principal = principals.get(username);

-        boolean validated;
-        if (principal == null) {
-            validated = false;
-        } else {
-            if (credentials == null || principal.getPassword() == null) {
-                if (log.isDebugEnabled())
-
log.debug(sm.getString("memoryRealm.authenticateFailure", username));
-                return (null);
+        boolean validated = false;
+        if (principal != null && credentials != null) {
+            if (hasMessageDigest()) {
+                // Hex hashes should be compared case-insensitive
+                validated = (digest(credentials)
+                             .equalsIgnoreCase(principal.getPassword()));
+            } else {
+                validated =
+                    (digest(credentials).equals(principal.getPassword()));
             }
-            validated = getCredentialHandler().matches(credentials,
principal.getPassword());
         }

         if (validated) {

On Tue, Mar 8, 2016 at 3:43 PM, Jason Overland <jasonoverl...@gmail.com> wrote:
> Hi, I'm upgrading an existing Vaadin 6 application from Tomcat 7.0.26
> to Tomcat 8.0.32 and have some questions.  I'm using Windows 7 and
> debugging in Eclipse.
>
> For authentication our configuration is using a MemoryRealm with
> digest="SHA".  We are storing usernames and passwords in a
> tomcat-users.xml file.  We are using a jaas.config which specifies to
> use a org.apache.catalina.realm.JAASMemoryLoginModule.  We have our
> own implementation of a CallbackModule.  For the upgrade I have added
> a CredentialHandler node inside our Realm as suggested in the
> documentation.
>
> server.xml:
>
>   <GlobalNamingResources>
>     <!-- Editable user database that can also be used by
>          UserDatabaseRealm to authenticate users
>     -->
>     <Resource auth="Container" description="User database that can be
> updated and saved"
> factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
> name="UserDatabase" pathname="conf/tomcat-users.xml"
> type="org.apache.catalina.UserDatabase"/>
>   </GlobalNamingResources>
>
>   ...
>
>   <Realm className="org.apache.catalina.realm.MemoryRealm" digest="SHA">
>     <CredentialHandler algorithm="SHA"
> className="org.apache.catalina.realm.MessageDigestCredentialHandler"/>
>   </Realm>
>
>
> jaas.config:
> /** JAAS Login Configuration for the Application **/
>
> JAASTomcat {
>    org.apache.catalina.realm.JAASMemoryLoginModule required debug=true;
> };
>
> our LoginController:
>
> callbackHandler = new LoginFormCallBackHandler(user);
> tomcatLoginContext = new LoginContext("JAASTomcat", callbackHandler);
> ...
> tomcatLoginContext.login();
>
>
> our CallbackHandler:
> public class LoginFormCallBackHandler implements CallbackHandler,
> Serializable  {
>
>     private static final long serialVersionUID = 1L;
>     private LoginEvent logins = null;
>     private User user;
>     public LoginFormCallBackHandler(User user)
>     {
>     this.user = user;
>     }
>
>     public void setLogins(LoginEvent loginEvent) {
>         logins = loginEvent;
>     }
>
>     public void handle(Callback[] callbacks) throws IOException,
>             UnsupportedCallbackException {
>         boolean gotName = false;
>     for (int i = 0; i < callbacks.length; i++) {
>             if (callbacks[i] instanceof NameCallback) {
>                 // get the username from the loginform event
>                 NameCallback nc = (NameCallback) callbacks[i];
>
> nc.setName(logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_USERNAME));
>                 user.username = nc.getName();
>                 gotName = true;
>             } else if (callbacks[i] instanceof TextInputCallback) {
>             TextInputCallback cb = (TextInputCallback) callbacks[i];
>             if (cb.getPrompt().equals("catalinaBase")) {
>                     cb.setText(System.getenv("CATALINA_BASE"););
>                 }
>             } else if (callbacks[i] instanceof PasswordCallback) {
>                 // get the password from the loginform event
>                 PasswordCallback pc = (PasswordCallback) callbacks[i];
>                 try {
>                 MessageDigest md = MessageDigest.getInstance("SHA");
>
> md.update(logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_PASSWORD).getBytes());
>                     user.password = String.format("%x", new
> BigInteger(1, md.digest()));
>                 }
>                 catch (NoSuchAlgorithmException nsae)
>                 {
>                 user.password =
> logins.getLoginParameter(LoginView.LOGINFORM_PARAMETER_PASSWORD);
>                 }
>                 pc.setPassword(user.password.toCharArray());
>             } else {
>                 System.out.println("Unrecognized Callback: " + callbacks[i]);
>             }
>         }
>         if (gotName)
>         logins = null;
>     }
> }
>
> This all worked in Tomcat 7 but unfortunately isn't working in Tomcat
> 8.  I was getting "WARNING: Unable to determine Catalina base to load
> file conf/tomcat-users.xml" and so I had to start responding to
> TextInputCallback "catalinaBase" as required by the new
> getCatalinaBase method on JAASMemoryLoginModule.
>
> I got past that and now I'm getting a NullPointerException:
> Authentication failed: java.lang.NullPointerException
> at org.apache.catalina.realm.MemoryRealm.authenticate(MemoryRealm.java:127)
> at 
> org.apache.catalina.realm.JAASMemoryLoginModule.login(JAASMemoryLoginModule.java:288)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> at java.lang.reflect.Method.invoke(Unknown Source)
> at javax.security.auth.login.LoginContext.invoke(Unknown Source)
> at javax.security.auth.login.LoginContext.access$000(Unknown Source)
> at javax.security.auth.login.LoginContext$4.run(Unknown Source)
> at javax.security.auth.login.LoginContext$4.run(Unknown Source)
> at java.security.AccessController.doPrivileged(Native Method)
> at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
> at javax.security.auth.login.LoginContext.login(Unknown Source)
> at com.lizardtech.es.adminui.vaadin.jaas.LoginController.onLogin(Unknown 
> Source)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> at java.lang.reflect.Method.invoke(Unknown Source)
> at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:510)
> at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164)
> at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1219)
> at com.vaadin.ui.LoginForm$2.handleParameters(LoginForm.java:103)
> at com.vaadin.ui.Window.handleParameters(Window.java:515)
> at 
> com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:528)
> at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
> at 
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
> at 
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
> at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
> at 
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
> at 
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
> at 
> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
> at 
> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
> at 
> org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
> at 
> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
> at 
> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
> at 
> org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
> at 
> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
> at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
> at 
> org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
> at 
> org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
> at 
> org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
> at 
> org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
> at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
> at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
> at 
> org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
> at java.lang.Thread.run(Unknown Source)
>
> Where the nullpointerexception happens on the following line in
> MemoryRealm.java:
>    validated = getCredentialHandler().matches(credentials,
> principal.getPassword());
>
> Debugging in Eclipse shows that credentialHandler is NULL here.  I've
> tried all combinations I can think of using the "digest" attribute on
> MemoryRealm and having a nested CredentialHandler and it's always
> NULL.
>
> I don't see anything interesting in my error logs.
>
> I have a couple questions:
> 1) How can I set the CredentialHandler on the MemoryRealm to avoid the
> NullPointerException?  I thought this would be set as a result of our
> server.xml having a CredentialHandler nested inside our MemoryRealm.
> From looking at the code of RealmBase (from which MemoryRealm
> inherits), I'm not even sure how it's possible to have a null
> credentialHandler as it's set in startInternal, if nothing else.  I
> don't know what startInternal is but it sounds like something that
> should be getting called.
> 2) Is JAASMemoryLoginModule part of a public API intended to be used
> by applications?  We are using it but I don't see it mentioned in the
> Tomcat docs.  Its javadoc says it's used "primarily for testing".
> 3) Is our existing configuration something that is supported in Tomcat 7/8?
> 4) I've been referring to the Tomcat documentation "Realm
> Configuration HOW-TO".  Both the 7 and 8 versions of the docs suggest
> that I need to use a JAASRealm and implement my own LoginModule.  Is
> this preferred over the configuration I currently have?
> 5) Is there a better way to do what I'm doing?
>
> Sorry for such a long email, but thanks for reading if you made it
> through.  Hope I'm not missing something glaringly obvious, but that's
> always a distinct possibility.

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

Reply via email to