Issue Details (XML | Word | Printable)

Key: GLASSFISH-20317
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: quang.dang
Reporter: arjan tijms
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
glassfish

JASPIC 1.1's new register session doesn't work

Created: 15/Apr/13 09:30 PM   Updated: 08/May/13 05:54 PM   Resolved: 08/May/13 05:54 PM
Component/s: security
Affects Version/s: 4.0_b84_RC1
Fix Version/s: 4.0_b88_RC4

Time Tracking:
Not Specified

Tags: 4_0-approved fishcat
Participants: arjan tijms, quang.dang, shreedhar_ganapathy, Tim Quinn and Tom Mueller


 Description  « Hide

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.



shreedhar_ganapathy added a comment - 16/Apr/13 02:24 PM

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


arjan tijms added a comment - 16/Apr/13 10:34 PM

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.


shreedhar_ganapathy added a comment - 19/Apr/13 05:22 PM - 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?


Tim Quinn added a comment - 19/Apr/13 05:48 PM - 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.


arjan tijms added a comment - 21/Apr/13 07:28 PM

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);

    // [...]
}

quang.dang added a comment - 26/Apr/13 08:46 PM

Investigating...


arjan tijms added a comment - 26/Apr/13 10:49 PM

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.


quang.dang added a comment - 01/May/13 02:21 PM

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


arjan tijms added a comment - 01/May/13 02:29 PM

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.


quang.dang added a comment - 01/May/13 02:57 PM

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.


arjan tijms added a comment - 01/May/13 03:27 PM

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.


arjan tijms added a comment - 01/May/13 04:19 PM

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).


arjan tijms added a comment - 01/May/13 08:27 PM

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.


quang.dang added a comment - 01/May/13 08:35 PM

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


quang.dang added a comment - 06/May/13 02:15 PM

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


Tom Mueller added a comment - 06/May/13 02:39 PM

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


quang.dang added a comment - 07/May/13 10:15 PM

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


arjan tijms added a comment - 08/May/13 08:13 AM

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!


quang.dang added a comment - 08/May/13 05:45 PM

trunk rev. 61899


quang.dang added a comment - 08/May/13 05:53 PM

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