FileDocCategorySizeDatePackage
LoginContextDriver.javaAPI DocGlassfish v2 API35258Fri May 04 22:35:26 BST 2007com.sun.enterprise.security.auth

LoginContextDriver

public class LoginContextDriver extends Object
This class is invoked implicitly by the server to log in the user information that was sent on the wire by the client. Clients will use the doClientLogin method to simulate authentication to the server.
author
Harpreet Singh (hsingh@eng.sun.com)
author
Jyri Virkki

Fields Summary
private static final Logger
_logger
private static final com.sun.enterprise.security.auth.login.ServerLoginCallbackHandler
dummyCallback
private static final String
CLIENT_JAAS_PASSWORD
private static final String
CLIENT_JAAS_CERTIFICATE
public static final String
CERT_REALMNAME
public static final com.sun.enterprise.security.audit.AuditManager
AUDIT_MANAGER
Constructors Summary
private LoginContextDriver()
This class cannot be instantiated

              
     
    
Methods Summary
private static voiddoAnonLogin()
A special case login for anonymous credentials (no login info).

        // instance of anononymous credential login with guest
       SecurityContext.setUnauthenticatedContext();
       if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE,"Set anonymous security context.");
        }  
    
private static voiddoCertificateLogin(javax.security.auth.Subject s)
A special case login for handling X509CertificateCredential. This does not get triggered based on current RI code. See X500Login.

        if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE, "Processing X509 certificate login.");
        }
        String realm = CertificateRealm.AUTH_TYPE;
        String user = null;
        try{
            Object obj = getPublicCredentials(s, X509CertificateCredential.class);

            X509CertificateCredential xp = (X509CertificateCredential) obj;
            user = xp.getAlias();
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Set security context as user: "+user);
            }
            setSecurityContext(user, s, realm);
            if(AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(user, realm, true);
            }
        } catch(LoginException le){
            if(AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(user, realm, false);
            }
            throw le;
        }
    
public static javax.security.auth.SubjectdoClientLogin(int type, javax.security.auth.callback.CallbackHandler jaasHandler)
Perform login on the client side. It just simulates the login on the client side. The method uses the callback handlers and generates correct credential information that will be later sent to the server

param
int type whether it is username_password or certificate based login.
param
CallbackHandler the callback handler to gather user information.
exception
LoginException the exception thrown by the callback handler.

        final javax.security.auth.callback.CallbackHandler handler =
            jaasHandler;
        // the subject will actually be filled in with a PasswordCredential
        // required by the csiv2 layer in the LoginModule.
        // we create the dummy credential here and call the 
        // set security context. Thus, we have 2  credentials, one each for
        // the csiv2 layer and the other for the RI.
        final Subject subject = new Subject();
        if (type == AppContainer.USERNAME_PASSWORD){
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                    try{
                        LoginContext lg = 
                            new LoginContext(CLIENT_JAAS_PASSWORD, 
                                             subject, handler);
                        lg.login();
                    }catch(javax.security.auth.login.LoginException e){
                        throw (LoginException)
                            new LoginException(e.toString()).initCause(e);
                    }
                    
                    return null;
                }
            });
            postClientAuth(subject, PasswordCredential.class);
            return subject;
        } else if (type == AppContainer.CERTIFICATE){
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                    try{
                        LoginContext lg = 
                            new LoginContext(CLIENT_JAAS_CERTIFICATE,
                                             subject, handler);
                        lg.login();
                    }catch(javax.security.auth.login.LoginException e){
                        throw (LoginException)
                            new LoginException(e.toString()).initCause(e);
                    }
                    
                    return null;
                }
            });
            postClientAuth(subject, X509CertificateCredential.class);
            return subject;
        } else if (type == AppContainer.ALL){
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                    try{
                        LoginContext lgup =
                            new LoginContext(CLIENT_JAAS_PASSWORD,
                                             subject, handler);
                        LoginContext lgc = 
                            new LoginContext(CLIENT_JAAS_CERTIFICATE,
                                                 subject, handler);
                        lgup.login();
                        postClientAuth(subject, PasswordCredential.class);
                        
                        lgc.login();
                        postClientAuth(subject,
                                       X509CertificateCredential.class);
                    }catch(javax.security.auth.login.LoginException e){
                        throw (LoginException)
                            new LoginException(e.toString()).initCause(e);
                    }
                    
                    return null;
                }
            });
            return subject;
        } else{
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                    try{
                        LoginContext lg =
                            new LoginContext(CLIENT_JAAS_PASSWORD, 
                                             subject, handler);
                        lg.login();
                        postClientAuth(subject, PasswordCredential.class);
                    }catch(javax.security.auth.login.LoginException e){
                        throw (LoginException)
                            new LoginException(e.toString()).initCause(e);
                    }
                    return null;
                }
            });
            return subject;
        }
    
public static voiddoClientLogout()
Perform logout on the client side.

exception
LoginException

        unsetClientSecurityContext();
    
private static voiddoGSSUPLogin(javax.security.auth.Subject s)
A special case login for GSSUPName credentials.

       if(_logger.isLoggable(Level.FINE)){
             _logger.fine("Processing GSSUP login.");
       }
       String user = null;
       String realm = Realm.getDefaultRealm();
       try{
            Object obj = getPublicCredentials(s, GSSUPName.class);

            user = ((GSSUPName)obj).getUser();
    
            setSecurityContext(user, s, realm);
            if(AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(user, realm, true);        
            }
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("GSSUP login succeeded for : " + user);
            }
       } catch (LoginException le){
           if(AUDIT_MANAGER.isAuditOn()){
               AUDIT_MANAGER.authentication(user, realm, false);
           }
           throw le;
       }
    
private static voiddoPasswordLogin(javax.security.auth.Subject subject)
Log in subject with PasswordCredential. This is a generic login which applies to all login mechanisms which process PasswordCredential. In other words, any mechanism which receives an actual username, realm and password set from the client.

The realm contained in the credential is checked, and a JAAS LoginContext is created using a context name obtained from the appropriate Realm instance. The applicable JAAS LoginModule is initialized (based on the jaas login configuration) and login() is invoked on it.

RI code makes several assumptions which are retained here:

  • The PasswordCredential is stored as a private credential of the subject.
  • There is only one such credential present (actually, only the first one is relevant if more are present).

    param
    s Subject to be authenticated.
    throws
    LoginException Thrown if the login fails.

            final Subject s = subject;
            
            Object obj = getPrivateCredentials(s, PasswordCredential.class);
            assert obj != null;
            
            PasswordCredential p = (PasswordCredential) obj;
            String user = p.getUser();
            String pwd = p.getPassword();
            String realm = p.getRealm();
            String jaasCtx = null;
            try {
                jaasCtx = Realm.getInstance(realm).getJAASContext();
            } catch(Exception ex) {
                if( ex instanceof LoginException )
                    throw (LoginException)ex;
                else
                    throw (LoginException)new LoginException(ex.toString()).initCause(ex);
            }
    
            assert user != null;
            assert pwd != null;
            assert realm != null;
            assert jaasCtx != null;
    
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("Logging in user [" + user + "] into realm: " +
                             realm + " using JAAS module: "+jaasCtx);
            }
    
            try {
                // A dummyCallback is used to satisfy JAAS but it is never used.
                // name/pwd info is already contained in Subject's Credential
                LoginContext lg = new LoginContext(jaasCtx, s, dummyCallback);
                lg.login();
                
            } catch (Exception e) {
                if (_logger.isLoggable(Level.INFO)) {
                    _logger.log(Level.INFO, "java_security.audit_auth_refused",
                                user);
                } 
                if (_logger.isLoggable(Level.FINEST)) {
                    _logger.log(Level.FINEST, "doPasswordLogin fails", e);
                }
                if(AUDIT_MANAGER.isAuditOn()){
                    AUDIT_MANAGER.authentication(user, realm, false);
                }
                if( e instanceof LoginException )
                    throw (LoginException)e;
                else
                    throw (LoginException)
                        new LoginException("Login failed: " + e.toString()).initCause(e);
            }
            if(AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(user, realm, true);
            }
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("Password login succeeded for : " + user);
            }
    
            setSecurityContext(user, s, realm);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE, "Set security context as user: "+user);
            }
        
private static voiddoX500Login(javax.security.auth.Subject s)
A special case login for X500Name credentials. This is invoked for certificate login because the containers extract the X.500 name from the X.509 certificate before calling into this class.

       if(_logger.isLoggable(Level.FINE)){        
            _logger.fine("Processing X.500 name login.");
       }
       String user = null;
       String realm_name = null;
       try{
            X500Name x500name = (X500Name)getPublicCredentials(s, X500Name.class);
            user = x500name.getName();
        
            // In the RI-inherited implementation this directly creates
            // some credentials and sets the security context. This means
            // that the certificate realm does not get an opportunity to
            // process the request. While the realm will not do any
            // authentication (already done by this point) it can choose
            // to adjust the groups or principal name or other variables
            // of the security context. Of course, bug 4646134 needs to be
            // kept in mind at all times.

            Realm realm = Realm.getInstance(CertificateRealm.AUTH_TYPE);

            if (realm instanceof CertificateRealm) { // should always be true
    
                CertificateRealm certRealm = (CertificateRealm)realm;
                certRealm.authenticate(s, x500name);
                realm_name = CertificateRealm.AUTH_TYPE;
                if(AUDIT_MANAGER.isAuditOn()){
                    AUDIT_MANAGER.authentication(user, realm_name, true);
                }
            } else {            
                _logger.warning("certlogin.badrealm");            
                setSecurityContext(user, s, realm_name);
                realm_name = realm.getName();
            }
        
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("X.500 name login succeeded for : " + user);
            }
       } catch (LoginException le){
           if(AUDIT_MANAGER.isAuditOn()){
               AUDIT_MANAGER.authentication(user, realm_name, false);
           }
           throw le;
       } catch (Exception ex) {
           throw (LoginException)new LoginException(ex.toString()).initCause(ex);
       }
    
private static java.lang.ObjectgetPrivateCredentials(javax.security.auth.Subject subject, java.lang.Class cls)
Retrieve a private credential of the given type (java class) from the subject.

This method retains the RI assumption that only the first credential of the given type is used.

        final Subject s = subject;
        final Class cl = cls;
        
        final Set credset = (Set)
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                    return
                        s.getPrivateCredentials(cl);
                }
            });

        final Iterator iter = credset.iterator();

        if (!iter.hasNext()) {
           String credmsg = cls.toString();
           if(_logger.isLoggable(Level.FINER)){
                 _logger.finer("Expected private credential of type: "+
                              credmsg + " but none found.");
           }
            throw new LoginException("Expected private credential of type: "+
                                     credmsg + " but none found.");
        }

        // retrieve only first credential of give type
        Object obj = null;
        try{
            obj = AppservAccessController.doPrivileged(new PrivilegedAction(){
                public java.lang.Object run(){
                    return iter.next();
                }
            });
        } catch (Exception e){
            // should never come here 
            if( e instanceof LoginException )
                throw (LoginException)e;
            else
                throw (LoginException)
                    new LoginException("Failed to retrieve private credential: "+
                                     e.toString()).initCause(e);
        }

        return obj;
    
private static java.lang.ObjectgetPublicCredentials(javax.security.auth.Subject s, java.lang.Class cls)
Retrieve a public credential of the given type (java class) from the subject.

This method retains the RI assumption that only the first credential of the given type is used.

        Set credset = s.getPublicCredentials(cls);
        
        final Iterator iter = credset.iterator();

        if(!iter.hasNext()) {
            String credmsg = cls.toString();
            if(_logger.isLoggable(Level.FINER)){
                _logger.finer("Expected public credentials of type : " +
                          credmsg + " but none found.");
            }
            throw new LoginException("Expected public credential of type: "+
                                     credmsg + " but none found.");
        }
        
        Object obj = null;    
        try{
            obj = AppservAccessController.doPrivileged(new PrivilegedAction(){
                public java.lang.Object run(){
                    return iter.next();
                }
            });
        } catch (Exception e){
            // should never come here 
            if( e instanceof LoginException )
                throw (LoginException)e;
            else
                throw (LoginException)
                    new LoginException("Failed to retrieve public credential: "+
                                       e.toString()).initCause(e);
        }

        return obj;
    
public static javax.security.auth.SubjectjmacLogin(javax.security.auth.Subject subject, java.lang.String username, java.lang.String password, java.lang.String realmName)
Performs login for JMAC security. The difference between this method and others is that it just verifies whether the login will succeed in the given realm. It does not set the result of the authentication in the appserver runtime environment A silent return from this method means that the given user succeeding in authenticating with the given password in the given realm

param
subject
param
username
param
password
param
realmName the realm to authenticate under
returns
Subject on successful authentication
throws
LoginException


        if(realmName == null || !(Realm.isValidRealm(realmName))){
            realmName = Realm.getDefaultRealm();
        }
        if (subject == null) {
            subject = new Subject();
        }
        final Subject fs = subject;
        final PasswordCredential pc =
            new PasswordCredential(username, password, realmName);

        AppservAccessController.doPrivileged(new PrivilegedAction(){
            public java.lang.Object run(){
                fs.getPrivateCredentials().add(pc);
                return fs;
            }
        });

        String jaasCtx = null;
        try {
            jaasCtx = Realm.getInstance(realmName).getJAASContext();
        } catch(Exception ex) {
            if( ex instanceof LoginException )
                throw (LoginException)ex;
            else
                throw (LoginException)
                    new LoginException(ex.toString()).initCause(ex);
        }

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("jmac login user [" + username + "] into realm: " +
                         realmName + " using JAAS module: "+jaasCtx);
        }

        try{
            // A dummyCallback is used to satisfy JAAS but it is never used.
            // name/pwd info is already contained in Subject's Credential
            LoginContext lg = new LoginContext(jaasCtx, fs, dummyCallback);
            lg.login();
        } catch (Exception e) {
            if (_logger.isLoggable(Level.INFO)) {
                _logger.log(Level.INFO, "java_security.audit_auth_refused",
                            username);
            }
            if(AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(username, realmName, false);
            }

            if( e instanceof LoginException )
                throw (LoginException)e;
            else
                throw (LoginException)
                    new LoginException("Login failed: " + e.toString()).initCause(e);
        }
        if(AUDIT_MANAGER.isAuditOn()){
            AUDIT_MANAGER.authentication(username, realmName, true);
        }
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("jmac Password login succeeded for : " + username);
        }

        return subject;
        // do not set the security Context
    
public static javax.security.auth.SubjectjmacLogin(javax.security.auth.Subject subject, javax.security.auth.x500.X500Principal x500Principal)


        if (subject == null) {
            subject = new Subject();
        }

        final Subject fs = subject;

        String userName = "";
        try {
            final X500Name x500Name = new X500Name(
                x500Principal.getName(X500Principal.RFC1779));
            userName = x500Name.toString();

            AppservAccessController.doPrivileged(new PrivilegedAction(){
                public java.lang.Object run(){
                    fs.getPublicCredentials().add(x500Name);
                    return fs;
                }
            });

            Realm realm = Realm.getInstance(CertificateRealm.AUTH_TYPE);
            CertificateRealm certRealm = (CertificateRealm)realm;
            certRealm.authenticate(fs, x500Name);
        } catch(Exception ex) {
            if (_logger.isLoggable(Level.INFO)) {
                _logger.log(Level.INFO, "java_security.audit_auth_refused",
                            userName);
            }
            if (AUDIT_MANAGER.isAuditOn()){
                AUDIT_MANAGER.authentication(userName,
                    CertificateRealm.AUTH_TYPE, false);
            }
            if (ex instanceof LoginException) {
                throw (LoginException)ex;
            } else {
                throw (LoginException)
                    new LoginException(ex.toString()).initCause(ex);
            }
        }

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("jmac cert login succeeded for: " + userName);
        }

        if (AUDIT_MANAGER.isAuditOn()){
            AUDIT_MANAGER.authentication(userName,
                CertificateRealm.AUTH_TYPE, true);
        }
        // do not set the security Context

        return subject;
    
public static voidlogin(java.lang.String username, java.lang.String password, java.lang.String realmName)
This method is just a convenience wrapper for login(Subject, Class) method. It will construct a PasswordCredential class.

param
String username
param
String password
param
String realmName the name of the realm to login into, if realmName is null, we login into the default realm


        if(realmName == null || !(Realm.isValidRealm(realmName))){    
            realmName = Realm.getDefaultRealm();
        }         
        final Subject fs = new Subject();
        final PasswordCredential pc =
            new PasswordCredential(username, password, realmName);
        
        AppservAccessController.doPrivileged(new PrivilegedAction(){
            public java.lang.Object run(){
                fs.getPrivateCredentials().add(pc);
                return fs;
            }
        });

        LoginContextDriver.login(fs, PasswordCredential.class);
    
public static voidlogin(javax.security.auth.Subject subject, java.lang.Class cls)
This method performs the login on the server side.

This method is the main login method for S1AS. It is called with a Subject and the type (class) of credential which should be checked. The Subject must contain a credential of the specified type or login will fail.

While the implementation has been cleaned up, the login process still consists of a number of special cases which are treated separately at the realm level. In the future tighter JAAS integration could clean some of this up.

The following credential types are recognized at this time:

  • PasswordCredential - This is the general case for all login methods which rely on the client providing a name and password. It can be used with any realms/JAAS login modules which expect such data (e.g. file realm, LDAP realm, UNIX realm)
  • X509CertificateCredential - Special case for SSL client auth. Here authentication has already been done by the SSL subsystem so this login only creates a security context based on the certificate data.
  • AnonCredential - Unauthenticated session, set anonymous security context.
  • GSSUPName - Retrieve user and realm and set security context.
  • X500Name - Retrieve user and realm and set security context.

param
Subject the subject of the client
param
Class the class of the credential packaged in the subject.

        if (_logger.isLoggable(Level.FINEST)) {
            _logger.log(Level.FINEST,
                        "Processing login with credentials of type: "+
                        cls.toString());
        }
        
        if(cls.equals(PasswordCredential.class)) {
            doPasswordLogin(subject);

        } else if (cls.equals(X509CertificateCredential.class)) {
            doCertificateLogin(subject);
            
        } else if (cls.equals(AnonCredential.class)) {
            doAnonLogin();
            
        } else if (cls.equals(GSSUPName.class)) {
            doGSSUPLogin(subject);
            
        } else if (cls.equals(X500Name.class)) {
            doX500Login(subject);
            
        } else {
            _logger.log(Level.INFO, "java_security.unknown_credential",
                        cls.toString());
            throw new
                LoginException("Unknown credential type, cannot login.");
        }
    
public static voidloginPrincipal(java.lang.String username, java.lang.String realmName)
This method is used for logging in a run As principal. It creates a JAAS subject whose credential is to type GSSUPName. This is used primarily for runas


        // no realm provided, assuming default
        if(realmName == null || realmName.length() == 0){
            realmName = Realm.getDefaultRealm();
        }
        
        final Subject s = new Subject();

        final com.sun.enterprise.deployment.PrincipalImpl p 
            = new com.sun.enterprise.deployment.PrincipalImpl(username);

        final GSSUPName name = new GSSUPName(username, realmName);

        AppservAccessController.doPrivileged(new PrivilegedAction() {
            public java.lang.Object run() {
                s.getPrincipals().add(p);
                s.getPublicCredentials().add(name);
                return null;
            }
        });

        setSecurityContext(username, s, realmName);
    
public static voidlogout()
This method logs out the user by clearing the security context.

        unsetSecurityContext();
    
private static voidpostClientAuth(javax.security.auth.Subject subject, java.lang.Class clazz)
Extract the relevant username and realm information from the subject and sets the correct state in the security context. The relevant information is set into the Thread Local Storage from which then is extracted to send over the wire.

param
Subject the subject returned by the JAAS login.
param
Class the class of the credential object stored in the subject

        final Class clas = clazz;
        final Subject fs = subject;
        Set credset = 
            (Set) AppservAccessController.doPrivileged(new PrivilegedAction() {
                public java.lang.Object run() {
                if(_logger.isLoggable(Level.FINEST)){
                    _logger.log(Level.FINEST,"LCD post login subject :" + fs);
                }
                    return fs.getPrivateCredentials(clas);
                }
            });
        final Iterator iter = credset.iterator();
        while(iter.hasNext()) {
            Object obj = null;    
            try{
                obj = AppservAccessController.doPrivileged(new PrivilegedAction(){
                    public java.lang.Object run(){
                        return iter.next();
                    }
                });
            } catch (Exception e){
                // should never come here 
                _logger.log(Level.SEVERE,
                            "java_security.accesscontroller_action_exception",
                            e);
            }
            if(obj instanceof PasswordCredential) {
                PasswordCredential p = (PasswordCredential) obj;
                String user = p.getUser();
                if(_logger.isLoggable(Level.FINEST)){
                    String realm = p.getRealm();
                    _logger.log(Level.FINEST,"In LCD user-pass login:" +
                            user +" realm :" + realm);
                }
                setClientSecurityContext(user, fs);
                return;
            } else if (obj instanceof X509CertificateCredential){
                X509CertificateCredential p = (X509CertificateCredential) obj;
                String user = p.getAlias();
                if(_logger.isLoggable(Level.FINEST)){
                    String realm = p.getRealm();
                    _logger.log(Level.FINEST,"In LCD cert-login::" +
                                user +" realm :" + realm);
                }
                setClientSecurityContext(user, fs);
                return;
            }
        }
    
private static voidsetClientSecurityContext(java.lang.String username, javax.security.auth.Subject subject)
Sets the security context on the appclient side. It sets the relevant information into the TLS

param
String username is the user who authenticated
param
Subject is the subject representation of the user
param
Credentials the credentials that the server associated with it

                                                 
        ClientSecurityContext securityContext =
            new ClientSecurityContext(username, subject);
        ClientSecurityContext.setCurrent(securityContext);
    
private static voidsetSecurityContext(java.lang.String userName, javax.security.auth.Subject subject, java.lang.String realm)
This method sets the security context on the current Thread Local Storage

param
String username is the user who authenticated
param
Subject is the subject representation of the user
param
Credentials the credentials that the server associated with it

 

        SecurityContext securityContext =
            new SecurityContext(userName, subject, realm);
        SecurityContext.setCurrent(securityContext);
    
private static voidunsetClientSecurityContext()
Unsets the current appclient security context on the Thread Local Storage

        ClientSecurityContext.setCurrent(null);
    
private static voidunsetSecurityContext()
Set the current security context on the Thread Local Storage to null.

        SecurityContext.setCurrent((SecurityContext)null);