Uploaded image for project: 'Application Server 7'
  1. Application Server 7
  2. AS7-5735

WebJASPIAuthenticator ignores GroupPrincipalCallback but requires PasswordValidationCallback

    Details

    • Steps to Reproduce:
      Hide

      1. Install a ServerAuthModule that contains the following code:

          @Override
          public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {
             
              CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, "test");
              GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(clientSubject, new String[] {"somerole"});
              
              try {
                  handler.handle( new Callback[] {callerPrincipalCallback, groupPrincipalCallback});
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (UnsupportedCallbackException e) {
                  e.printStackTrace();
              }
       
              return AuthStatus.SUCCESS;
          }
      

      2. Access a protected resource

      Show
      1. Install a ServerAuthModule that contains the following code: @Override public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, "test"); GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(clientSubject, new String[] {"somerole"}); try { handler.handle( new Callback[] {callerPrincipalCallback, groupPrincipalCallback}); } catch (IOException e) { e.printStackTrace(); } catch (UnsupportedCallbackException e) { e.printStackTrace(); }   return AuthStatus.SUCCESS; } 2. Access a protected resource

      Description

      In JBoss AS 7.1.1, if a user provided ServerAuthModule provides a GroupPrincipalCallback, then this is ignored by WebJASPIAuthenticator. The provided handler copies the GroupPrincipalCallback, but the authenticator then does nothing with it. Simulteanously, if the ServerAuthModule does not provide a PasswordValidationCallback to the handler, then this will result in a null pointer exception in the authenticator.

      Regarding the ignored GroupPrincipalCallback, the problem seems to be in the following code:

      WebJASPIAuthenticator#authenticate

      // ...
             if (result) {
                  PasswordValidationCallback pvc = cbh.getPasswordValidationCallback();
                  CallerPrincipalCallback cpc = cbh.getCallerPrincipalCallback();
       
                  // get the client principal from the callback.
                  Principal clientPrincipal = cpc.getPrincipal();
                  if (clientPrincipal == null) {
                      clientPrincipal = new SimplePrincipal(cpc.getName());
                  }
       
                  // if the client principal is not a jboss generic principal, we need to build one before registering.
                  if (!(clientPrincipal instanceof JBossGenericPrincipal))
                      clientPrincipal = this.buildJBossPrincipal(clientSubject, clientPrincipal);
      

      buildJBossPrincipal() looks at the "Roles" group in the Subject, but this hasn't been set by either the handler or other code based on what the GroupPrincipalCallback contains.

      I wonder if changing this into the following would be more correct:

      WebJASPIAuthenticator#authenticate

      // ...
             if (result) {
                  PasswordValidationCallback pvc = cbh.getPasswordValidationCallback();
                  CallerPrincipalCallback cpc = cbh.getCallerPrincipalCallback();
                  GroupPrincipalCallback gpc = cbh.getGroupPrincipalCallback(); // ADDED
       
                  // get the client principal from the callback.
                  Principal clientPrincipal = cpc.getPrincipal();
                  if (clientPrincipal == null) {
                      clientPrincipal = new SimplePrincipal(cpc.getName());
                  }
       
                  // if the client principal is not a jboss generic principal, we need to build one before registering.
                  if (!(clientPrincipal instanceof JBossGenericPrincipal))
                      clientPrincipal = this.buildJBossPrincipal(clientSubject, clientPrincipal, gpc); // ADDED gpc PARAMETER
      

      With buildJBossPrincipal() implemented as:

      WebJASPIAuthenticator#buildJBossPrincipal

          protected Principal buildJBossPrincipal(Subject subject, Principal principal, GroupPrincipalCallback groupPrincipalCallback) {
       
              List<String> roles = new ArrayList<String>();
              // look for roles in the subject first.
              for (Principal p : subject.getPrincipals()) {
                  if (p instanceof Group && p.getName().equals("Roles")) {
                      Enumeration<? extends Principal> members = ((Group) p).members();
                      while (members.hasMoreElements())
                          roles.add(members.nextElement().getName());
                  }
              }
              
              // START ADDED
              if (groupPrincipalCallback != null && groupPrincipalCallback.getGroups() != null) {
                  for (String group : groupPrincipalCallback.getGroups()) {
                      roles.add(group);
                  }
              }
              // END ADDED
       
              // if the subject didn't contain any roles, look for the roles declared in the deployment descriptor.
              JBossWebRealm realm = (JBossWebRealm) this.getContainer().getRealm();
              Set<String> descriptorRoles = realm.getPrincipalVersusRolesMap().get(principal.getName());
              if (roles.isEmpty() && descriptorRoles != null)
                  roles.addAll(descriptorRoles);
       
              // build and return the JBossGenericPrincipal.
              return new JBossGenericPrincipal(realm, principal.getName(), null, roles, principal, null, null, null, subject);
       
          }
      

      As for the PasswordValidationCallback, WebJASPIAuthenticator now contains the following code in authenticate():

      WebJASPIAuthenticator#authenticate

      this.register(request, response, clientPrincipal, authMethod, pvc.getUsername(),
          new String(pvc.getPassword()));
      

      The register() method considers both username and password as optional, but because there's no null check on pvc, the above line will throw a NPE in case no PasswordValidationCallback is provided. This could perhaps be changed into something like the following:

      WebJASPIAuthenticator#authenticate

      this.register(request, response, clientPrincipal, authMethod, pvc != null ? pvc.getUsername() : null,
          pvc != null && pvc.getPassword() != null ? new String(pvc.getPassword()) : null);
      

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  sguilhen Stefan Guilhen
                  Reporter:
                  atijms arjan tijms
                • Votes:
                  2 Vote for this issue
                  Watchers:
                  4 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: