glassfish
  1. glassfish
  2. GLASSFISH-20317

JASPIC 1.1's new register session doesn't work

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 4.0_b84_RC1
    • Fix Version/s: 4.0_b88_RC4
    • Component/s: security
    • Labels:
      None

      Description

      In JASPIC 1.1 a new feature was specified that allows a SAM to ask the runtime to register a session. See http://java.net/jira/browse/JASPIC_SPEC-3 and http://jcp.org/aboutJava/communityprocess/maintenance/jsr196/module-asking-for-container-auth-session.pdf

      Changes for this were made to GlassFish 4, but in practice they don't seem to work.

      From a SAM's validateRequest method I called the following code:

      public static void setRegisterSession(MessageInfo messageInfo) {
          messageInfo.getMap().put("javax.servlet.http.registerSession", TRUE.toString());
      }
      

      An authenticated identity was set via code such as the following:

      CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, "test");
      GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(
          clientSubject, new String[] { "architect" }
      );
       
      try {
          handler.handle(new Callback[] { callerPrincipalCallback, groupPrincipalCallback });
      } catch (IOException | UnsupportedCallbackException e) {
          e.printStackTrace();
      }
       
      return SUCCESS;
      

      After this a protected resource could indeed be invoked, but after requesting the same protected resource again, the SAM was also invoked again without any trace from the previously established authenticated identity. If I'm not mistaken the idea is that the runtime remembers this authenticated identity (name + groups/roles) and will not invoke the SAM again until the user explicitly log outs, or removes the HTTP session.

      p.s. I also tested on GlassFish 3.1.2.2 using the proprietary key com.sun.web.RealmAdapter.register and checked by stepping into the GlassFish source code that the "register" branch is indeed taken in RealmAdapter:

      if (register) {
            AuthenticatorProxy proxy = new AuthenticatorProxy(authenticator, wp, authType);
            proxy.authenticate(request, response, config);
      } else {
             request.setAuthType((authType == null) ? PROXY_AUTH_TYPE : authType);
             request.setUserPrincipal(wp);
      }
      

      There too the authenticated identity was not remembered.

        Activity

        Hide
        shreedhar_ganapathy added a comment -

        Jeff, could you have someone from your team look into this?

        Show
        shreedhar_ganapathy added a comment - Jeff, could you have someone from your team look into this?
        Hide
        arjan tijms added a comment -

        I asked Ron Monzillo about this issue as well, and he kindly provided some clarification about the usage of this feature.

        As it appears, the container is not supposed to fully automatically establish the authenticated identity, but it still calls the SAM with the previously authenticated identity available from request.getUserPrincipal. This should be passed into the CallerPrincipalHandler again in order to re-authenticate.

        However, it still doesn't fully work. The userPrincpal's name is indeed restored, but any previously set groups are not restored. From the mail exchange with Ron:

        In the case of glassfish, if you have the callback handler handle a CPC constructed with the principal obtained from getCallerPrincipal, and the principal was originally established using a CPC constructed with the principal name, then the groups should be restablished from the container specific principal.

        I tested with the approach you outlined on both GlassFish 3.1.2.2 and GlassFish 4 (b84) with the old resp. new key.

        The good news is that it indeed works for the principal. request.getUserPrincipal indeed returns a non-null principal, which can be passed into the CBH and then the authenticated identity is established.

        The bad news is that on both versions of GlassFish it does not seem to work for the groups.

        This is the code I used in the beginning of the validateRequest method:

           Principal userPrincipal = request.getUserPrincipal();
                
                if (userPrincipal != null) {
                    
                    try {
                      
                        handler.handle(new Callback[] { 
                            new CallerPrincipalCallback(clientSubject, userPrincipal) }
                        );
                        
                        return SUCCESS;
                        
                    } catch (IOException | UnsupportedCallbackException e) {
                        // Should not happen
                        throw new IllegalStateException(e);
                    }
                }
        

        If I try to access a protected resource again (one that I could access after authentication first succeeded by using both the CallerPrincipalCallback and the GroupPrincipalCallback), access is denied and a 403 is returned.

        If I then try to access a non-protected resource and print out the the userPrincipal and the group/role that was used before, then the userPrincipal returns the correct name, but the group is false.

        E.g. by putting the following in a very basic JSP page:

        <%=request.getUserPrincipal() %>
        
        <%=request.isUserInRole("architect") %> 
        

        Using a debugger I can see that in the SAM the associated SecurityContext (secCtx in the WebPrincipal) does have the right group.

        Show
        arjan tijms added a comment - I asked Ron Monzillo about this issue as well, and he kindly provided some clarification about the usage of this feature. As it appears, the container is not supposed to fully automatically establish the authenticated identity, but it still calls the SAM with the previously authenticated identity available from request.getUserPrincipal . This should be passed into the CallerPrincipalHandler again in order to re-authenticate. However, it still doesn't fully work. The userPrincpal's name is indeed restored, but any previously set groups are not restored. From the mail exchange with Ron: In the case of glassfish, if you have the callback handler handle a CPC constructed with the principal obtained from getCallerPrincipal, and the principal was originally established using a CPC constructed with the principal name, then the groups should be restablished from the container specific principal. I tested with the approach you outlined on both GlassFish 3.1.2.2 and GlassFish 4 (b84) with the old resp. new key. The good news is that it indeed works for the principal. request.getUserPrincipal indeed returns a non-null principal, which can be passed into the CBH and then the authenticated identity is established. The bad news is that on both versions of GlassFish it does not seem to work for the groups. This is the code I used in the beginning of the validateRequest method: Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null ) { try { handler.handle( new Callback[] { new CallerPrincipalCallback(clientSubject, userPrincipal) } ); return SUCCESS; } catch (IOException | UnsupportedCallbackException e) { // Should not happen throw new IllegalStateException(e); } } If I try to access a protected resource again (one that I could access after authentication first succeeded by using both the CallerPrincipalCallback and the GroupPrincipalCallback ), access is denied and a 403 is returned. If I then try to access a non-protected resource and print out the the userPrincipal and the group/role that was used before, then the userPrincipal returns the correct name, but the group is false. E.g. by putting the following in a very basic JSP page: <%=request.getUserPrincipal() %> <%=request.isUserInRole( "architect" ) %> Using a debugger I can see that in the SAM the associated SecurityContext (secCtx in the WebPrincipal) does have the right group.
        Hide
        shreedhar_ganapathy added a comment - - edited

        Hi Tim
        Do you have an eta for this fix? Any chance this will make it before next promoted build i.e Tuesday 4/23?

        Show
        shreedhar_ganapathy added a comment - - edited Hi Tim Do you have an eta for this fix? Any chance this will make it before next promoted build i.e Tuesday 4/23?
        Hide
        Tim Quinn added a comment - - edited

        Shreedhar,

        I believe Jeff is aware that I'm not able to work on this.

        I'll go ahead and reassign this back to him to avoid any misunderstanding.

        Show
        Tim Quinn added a comment - - edited Shreedhar, I believe Jeff is aware that I'm not able to work on this. I'll go ahead and reassign this back to him to avoid any misunderstanding.
        Hide
        arjan tijms added a comment -

        I'm not 100% sure if this is the right approach, but modifying com.sun.enterprise.security.jmac.callback.BaseContainerCallbackHandler as follows makes the feature work for the simple test case explained above:

        private void processCallerPrincipal(CallerPrincipalCallback cpCallback) {
            final Subject fs = cpCallback.getSubject();
            Principal principal = cpCallback.getPrincipal();
        
            String realmName = null;
            if (handlerContext != null) {
                realmName = handlerContext.getRealmName();
            }
        
            // Start added
            
            if (principal instanceof WebPrincipal) {
                
                @SuppressWarnings("unchecked")
                Set<Principal> principals = ((WebPrincipal) principal).getSecurityContext().getPrincipalSet();
                
                if (principals != null && !principals.isEmpty()) {
                    List<String> groups = new ArrayList<String>();
                    for (Principal webOwnedPrincipal : principals) {
                        if (webOwnedPrincipal instanceof Group) {
                            groups.add(webOwnedPrincipal.getName());
                        }
                    }
                    processGroupPrincipal(new GroupPrincipalCallback(fs, groups.toArray(new String[groups.size()])));
                } 
            }
        
            // End added
        
            boolean isCertRealm = CertificateRealm.AUTH_TYPE.equals(realmName);
        
            // [...]
        }
        
        Show
        arjan tijms added a comment - I'm not 100% sure if this is the right approach, but modifying com.sun.enterprise.security.jmac.callback.BaseContainerCallbackHandler as follows makes the feature work for the simple test case explained above: private void processCallerPrincipal(CallerPrincipalCallback cpCallback) { final Subject fs = cpCallback.getSubject(); Principal principal = cpCallback.getPrincipal(); String realmName = null ; if (handlerContext != null ) { realmName = handlerContext.getRealmName(); } // Start added if (principal instanceof WebPrincipal) { @SuppressWarnings( "unchecked" ) Set<Principal> principals = ((WebPrincipal) principal).getSecurityContext().getPrincipalSet(); if (principals != null && !principals.isEmpty()) { List< String > groups = new ArrayList< String >(); for (Principal webOwnedPrincipal : principals) { if (webOwnedPrincipal instanceof Group) { groups.add(webOwnedPrincipal.getName()); } } processGroupPrincipal( new GroupPrincipalCallback(fs, groups.toArray( new String [groups.size()]))); } } // End added boolean isCertRealm = CertificateRealm.AUTH_TYPE.equals(realmName); // [...] }
        Hide
        quang.dang added a comment -

        Investigating...

        Show
        quang.dang added a comment - Investigating...
        Hide
        arjan tijms added a comment -

        Investigating...

        Quang dang, thanks for looking into this! if there's any way I can help, either by testing, debugging or something else, please let me know.

        Show
        arjan tijms added a comment - Investigating... Quang dang, thanks for looking into this! if there's any way I can help, either by testing, debugging or something else, please let me know.
        Hide
        quang.dang added a comment -

        Arjan, how many requests to the protected-resource do you have to make from the browser before seeing the first 403 response ?

        Show
        quang.dang added a comment - Arjan, how many requests to the protected-resource do you have to make from the browser before seeing the first 403 response ?
        Hide
        arjan tijms added a comment -

        how many requests to the protected-resource do you have to make from the browser before seeing the first 403 response?

        It's the very first request AFTER the one in which the initial authentication was done.

        So, the SAM will check for userPrincipal != null. If it is null, it does the authentication by using the handler normally. Meaning, it will use both the CallerPrincipalCallback and the GroupPrincipalCallback for the initial authentication. It then returns SUCCESS and the protected resource is displayed.

        After that, the very first request to the same protected resource will generate the 403.

        Show
        arjan tijms added a comment - how many requests to the protected-resource do you have to make from the browser before seeing the first 403 response? It's the very first request AFTER the one in which the initial authentication was done. So, the SAM will check for userPrincipal != null . If it is null, it does the authentication by using the handler normally. Meaning, it will use both the CallerPrincipalCallback and the GroupPrincipalCallback for the initial authentication. It then returns SUCCESS and the protected resource is displayed. After that, the very first request to the same protected resource will generate the 403.
        Hide
        quang.dang added a comment -

        I understand the initial auth is done on the first request and I expect to get 403 on the second request. However that is not what I am seeing. I see 403 on the third request. For some reason, on the second request, request.getUserPrincipal() still returns null. Maybe it's just my environment or maybe I'm seeing another bug. I've observed this behavior consistently after many server restarts.

        Show
        quang.dang added a comment - I understand the initial auth is done on the first request and I expect to get 403 on the second request. However that is not what I am seeing. I see 403 on the third request. For some reason, on the second request, request.getUserPrincipal() still returns null. Maybe it's just my environment or maybe I'm seeing another bug. I've observed this behavior consistently after many server restarts.
        Hide
        arjan tijms added a comment -

        Hmmm, that's odd. I very consistently see it on the second request. For my test app I've added my test SAM programmatically instead of installing it inside GlassFish. Maybe that's the difference? I've tested with both GlassFish 3.1.2.2 (using the old proprietary key name) and GlassFish 4.0 build 83 and 84.

        I'll try to put together a minimal example and post it back here.

        Show
        arjan tijms added a comment - Hmmm, that's odd. I very consistently see it on the second request. For my test app I've added my test SAM programmatically instead of installing it inside GlassFish. Maybe that's the difference? I've tested with both GlassFish 3.1.2.2 (using the old proprietary key name) and GlassFish 4.0 build 83 and 84. I'll try to put together a minimal example and post it back here.
        Hide
        arjan tijms added a comment -

        So this is rather interesting...

        In a minimal example I'm seeing the same behavior that you mentioned now. I've published it at: https://github.com/arjantijms/glassfish-20317

        It's a Maven project for a web (war) project, with an embedded SAM. Just build and deploy it. Then

        In a more complex SAM where the SAM is already invoked a couple of times (due to a message exchange with the user), the principal is NOT null after the first request after the initial authentication. I've published the code for this as well at: https://github.com/arjantijms/two-factor-sam (it's only the separate SAM).

        Show
        arjan tijms added a comment - So this is rather interesting... In a minimal example I'm seeing the same behavior that you mentioned now. I've published it at: https://github.com/arjantijms/glassfish-20317 It's a Maven project for a web (war) project, with an embedded SAM. Just build and deploy it. Then Request http://localhost:8080/glassfish-20317/protected/a.jsp . The SAM will immediately authenticate and access to the page will be granted. Request http://localhost:8080/glassfish-20317/protected/a.jsp again, and the principal will be null indeed! The SAM will then do a full "initial" authentication again. Now request http://localhost:8080/glassfish-20317/protected/a.jsp for the third time, and then the principal will NOT be null, and after the re-authentication protocol is executed access will NOT be granted since the roles/groups are not being restored. Request http://localhost:8080/glassfish-20317/index.jsp to see that the UserPrincipal has the value "test", but the required role "architect" has not been assigned. In a more complex SAM where the SAM is already invoked a couple of times (due to a message exchange with the user), the principal is NOT null after the first request after the initial authentication. I've published the code for this as well at: https://github.com/arjantijms/two-factor-sam (it's only the separate SAM).
        Hide
        arjan tijms added a comment -

        In case it might be helpful, I've created a self-contained web app for the test case with the SAM I used previously as well. It's published at: https://github.com/arjantijms/glassfish-20317b

        It's again a Maven project for a web (war) project, with an embedded SAM. Just build and deploy it. Then

        • Request http://localhost:8080/glassfish-20317b/protected/a.jsp. The SAM will redirect to /login.jsp.
        • Enter foo for the username and bar for the password. The SAM will redirect to /token.jsp.
        • Enter abc for the token. The SAM will redirect to /protected/a.jsp and during processing of /protected/a.jsp will authenticate and access to the page will be granted.
        • Request http://localhost:8080/glassfish-20317b/protected/a.jsp again, and the principal will NOT be null, and after the re-authentication protocol is executed access will NOT be granted since the roles/groups are not being restored.

        Just to be sure I've re-tried this a couple of times, and every time the first request after the one in which authentication has taken place has a non-NULL principal, while in the https://github.com/arjantijms/glassfish-20317 example the first request after the authentication has a NULL principal, and only at second request it's non-NULL.

        Show
        arjan tijms added a comment - In case it might be helpful, I've created a self-contained web app for the test case with the SAM I used previously as well. It's published at: https://github.com/arjantijms/glassfish-20317b It's again a Maven project for a web (war) project, with an embedded SAM. Just build and deploy it. Then Request http://localhost:8080/glassfish-20317b/protected/a.jsp . The SAM will redirect to /login.jsp . Enter foo for the username and bar for the password. The SAM will redirect to /token.jsp . Enter abc for the token. The SAM will redirect to /protected/a.jsp and during processing of /protected/a.jsp will authenticate and access to the page will be granted. Request http://localhost:8080/glassfish-20317b/protected/a.jsp again, and the principal will NOT be null, and after the re-authentication protocol is executed access will NOT be granted since the roles/groups are not being restored. Just to be sure I've re-tried this a couple of times, and every time the first request after the one in which authentication has taken place has a non-NULL principal, while in the https://github.com/arjantijms/glassfish-20317 example the first request after the authentication has a NULL principal, and only at second request it's non-NULL.
        Hide
        quang.dang added a comment -

        Bug filed for the null principal: https://java.net/jira/browse/GLASSFISH-20451

        Show
        quang.dang added a comment - Bug filed for the null principal: https://java.net/jira/browse/GLASSFISH-20451
        Hide
        quang.dang added a comment -

        What is the impact on the customer of the bug?
        This is to implement the session registration feature in JASPIC 1.1.

        What is the cost/risk of fixing the bug?
        This is a low-medium risk bug. It should be fixed/implemented to satisfy the JASPIC spec requirement.

        Is there an impact on documentation or message strings?
        No

        Which tests should QA (re)run to verify the fix did not destabilize GlassFish?
        JASPIC related tests.

        Which is the targeted build of 4.0 for this fix?
        4.0_b88

        Show
        quang.dang added a comment - What is the impact on the customer of the bug? This is to implement the session registration feature in JASPIC 1.1. What is the cost/risk of fixing the bug? This is a low-medium risk bug. It should be fixed/implemented to satisfy the JASPIC spec requirement. Is there an impact on documentation or message strings? No Which tests should QA (re)run to verify the fix did not destabilize GlassFish? JASPIC related tests. Which is the targeted build of 4.0 for this fix? 4.0_b88
        Hide
        Tom Mueller added a comment -

        Approved for 4.0. Please be sure to check the fix in to the 4.0 branch and the trunk.

        Show
        Tom Mueller added a comment - Approved for 4.0. Please be sure to check the fix in to the 4.0 branch and the trunk.
        Hide
        quang.dang added a comment -

        4.0 revision 61884, appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jmac/callback/BaseContainerCallbackHandler.java

        Show
        quang.dang added a comment - 4.0 revision 61884, appserver/security/core-ee/src/main/java/com/sun/enterprise/security/jmac/callback/BaseContainerCallbackHandler.java
        Hide
        arjan tijms added a comment -

        Thanks for the fix Quang Dang!

        If you're still working on the code, please ignore the following, but I noticed that in rev 61884 of BaseContainerCallbackHandler#processCallerPrincipal there's a boolean useName set to false but thereafter not used anymore.

        When reading through reuseWebPrincipal (which is quite an impressive check), I noticed two tiny typos in the comments. Hardly worth mentioning really, but on line 263 it says "WebPrincipla" and on line 331 it says "remove any exiting".

        I'll see if I can do some local testing with the code later today. Thanks again!

        Show
        arjan tijms added a comment - Thanks for the fix Quang Dang! If you're still working on the code, please ignore the following, but I noticed that in rev 61884 of BaseContainerCallbackHandler#processCallerPrincipal there's a boolean useName set to false but thereafter not used anymore. When reading through reuseWebPrincipal (which is quite an impressive check), I noticed two tiny typos in the comments. Hardly worth mentioning really, but on line 263 it says "WebPrincipla" and on line 331 it says "remove any exiting". I'll see if I can do some local testing with the code later today. Thanks again!
        Hide
        quang.dang added a comment -

        trunk rev. 61899

        Show
        quang.dang added a comment - trunk rev. 61899
        Hide
        quang.dang added a comment -

        Ron gave some serious thought on this issue and came up with that fix.
        The spellings got corrected.

        Show
        quang.dang added a comment - Ron gave some serious thought on this issue and came up with that fix. The spellings got corrected.

          People

          • Assignee:
            quang.dang
            Reporter:
            arjan tijms
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: