/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.security.auth.login;
import org.glassfish.security.common.Group;
import java.util.Set;
import java.util.Iterator;
import java.util.Enumeration;
import java.security.Principal;
import java.util.logging.*;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.x500.X500Principal;
import sun.security.x509.X500Name;
import com.sun.logging.*;
import com.sun.enterprise.common.iiop.security.GSSUPName;
import com.sun.enterprise.common.iiop.security.AnonCredential;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.SecurityLoggerInfo;
import com.sun.enterprise.security.SecurityServicesUtil;
import com.sun.enterprise.security.audit.AuditManager;
import com.sun.enterprise.security.common.AppservAccessController;
//import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.auth.login.common.PasswordCredential;
import com.sun.enterprise.security.auth.login.common.X509CertificateCredential;
import com.sun.enterprise.security.auth.login.common.ServerLoginCallbackHandler;
import com.sun.enterprise.security.auth.login.common.LoginException;
import com.sun.enterprise.security.auth.realm.Realm;
import com.sun.enterprise.security.auth.realm.certificate.CertificateRealm;
// FIXME: ACC methods need to be moved to ACC-specific class.
import com.sun.enterprise.security.auth.realm.InvalidOperationException;
import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
import com.sun.enterprise.security.auth.realm.NoSuchUserException;
import com.sun.enterprise.security.common.ClientSecurityContext;
//import com.sun.enterprise.appclient.AppContainer;
import com.sun.enterprise.security.common.SecurityConstants;
import org.glassfish.internal.api.Globals;
/**
*
* 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 <i>doClientLogin</i> method to simulate authentication to the
* server.
*
* @author Harpreet Singh (hsingh@eng.sun.com)
* @author Jyri Virkki
*
*/
public class LoginContextDriver {
private static final Logger _logger = SecurityLoggerInfo.getLogger();
private static final ServerLoginCallbackHandler
dummyCallback = new ServerLoginCallbackHandler();
// moved to SecurityConstants.
// private static final String CLIENT_JAAS_PASSWORD = "default";
// private static final String CLIENT_JAAS_CERTIFICATE = "certificate";
public static final String CERT_REALMNAME = "certificate";
private static volatile AuditManager AUDIT_MANAGER=null;
/** This class cannot be instantiated
*
*/
private LoginContextDriver(){
}
private static AuditManager getAuditManager() {
if(AUDIT_MANAGER != null) {
return AUDIT_MANAGER;
}
return _getAuditManager();
}
private static synchronized AuditManager _getAuditManager() {
if(AUDIT_MANAGER == null) {
SecurityServicesUtil secServUtil = Globals.get(SecurityServicesUtil.class);
AUDIT_MANAGER = secServUtil.getAuditManager();
}
return AUDIT_MANAGER;
}
/**
* This method is just a convenience wrapper for
* <i>login(Subject, Class)</i> 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
*/
public static void login(String username, char[] password, String realmName){
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 void login(AssertedCredentials asrtCred) throws javax.security.auth.login.LoginException {
Subject subject = new Subject();
subject.getPrivateCredentials().add(asrtCred);
String jaasCtx = null;
try {
jaasCtx = Realm.getInstance(asrtCred.getRealmName()).getJAASContext();
} catch (Exception ex) {
if (ex instanceof LoginException) {
throw (LoginException) ex;
} else {
throw (LoginException) new LoginException(ex.toString()).initCause(ex);
}
}
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, subject, dummyCallback);
lg.login();
} catch (Exception e) {
if (_logger.isLoggable(Level.INFO)) {
_logger.log(Level.INFO, SecurityLoggerInfo.auditAtnRefusedError, asrtCred.getUserName());
}
if (_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.FINEST, "doPasswordLogin fails", e);
}
if (AUDIT_MANAGER.isAuditOn()) {
AUDIT_MANAGER.authentication(asrtCred.getUserName(), asrtCred.getRealmName(), false);
}
if (e instanceof LoginException) {
throw (LoginException) e;
} else {
throw (LoginException) new LoginException("Login failed: " + e.getMessage()).initCause(e);
}
}
setSecurityContext(asrtCred.getUserName(), subject, asrtCred.getRealmName());
}
/**
* This method performs the login on the server side.
*
* <P>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.
*
* <P>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.
*
* <P>The following credential types are recognized at this time:
* <ul>
* <li>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)
* <LI>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.
* <LI>AnonCredential - Unauthenticated session, set anonymous security
* context.
* <LI>GSSUPName - Retrieve user and realm and set security context.
* <LI>X500Name - Retrieve user and realm and set security context.
* </ul>
*
* @param Subject the subject of the client
* @param Class the class of the credential packaged in the subject.
*
*/
public static void login(Subject subject, Class cls)
throws LoginException
{
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, null);
} else {
_logger.log(Level.INFO, SecurityLoggerInfo.unknownCredentialError,
cls.toString());
throw new
LoginException("Unknown credential type, cannot login.");
}
}
/**
* 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
*
*/
public static void loginPrincipal(String username, String realmName)
throws LoginException {
// no realm provided, assuming default
if(realmName == null || realmName.length() == 0){
realmName = Realm.getDefaultRealm();
}
final Subject s = new Subject();
final org.glassfish.security.common.PrincipalImpl p
= new org.glassfish.security.common.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;
}
});
try {
Realm realm = Realm.getInstance(realmName);
Enumeration en = realm.getGroupNames(username);
Set<Principal> principalSet = s.getPrincipals();
while (en.hasMoreElements()) {
principalSet.add(new Group((String) en.nextElement()));
}
} catch (InvalidOperationException ex) {
_logger.log(Level.WARNING, SecurityLoggerInfo.invalidOperationForRealmError, new Object[] { username, realmName, ex.toString()});
} catch (NoSuchUserException ex){
_logger.log(Level.WARNING, SecurityLoggerInfo.noSuchUserInRealmError, new Object[] { username, realmName, ex.toString()});
} catch (NoSuchRealmException ex) {
LoginException lex = new LoginException(ex.toString());
lex.initCause(ex);
throw lex;
}
setSecurityContext(username, s, realmName);
}
/**
* This method logs out the user by clearing the security context.
*
*/
public static void logout() throws LoginException {
unsetSecurityContext();
}
/**
* 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.
*
* <P>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.
*
* <P>RI code makes several assumptions which are retained here:
* <ul>
* <li>The PasswordCredential is stored as a private credential of
* the subject.
* <li>There is only one such credential present (actually, only
* the first one is relevant if more are present).
* </ui>
*
* @param s Subject to be authenticated.
* @throws LoginException Thrown if the login fails.
*
*/
private static void doPasswordLogin(Subject subject)
throws LoginException
{
final Subject s = subject;
Object obj = getPrivateCredentials(s, PasswordCredential.class);
assert obj != null;
PasswordCredential p = (PasswordCredential) obj;
String user = p.getUser();
char[] 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.FINEST)) {
_logger.log(Level.FINEST, "doPasswordLogin fails", e);
}
if(getAuditManager() != null && getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm, false);
}
if( e instanceof LoginException )
throw (LoginException)e;
else
throw (LoginException)
new LoginException("Login failed: " + e.getMessage()).initCause(e);
}
if(getAuditManager() != null && getAuditManager().isAuditOn()){
getAuditManager().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);
}
}
/** 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
*/
public static Subject jmacLogin(Subject subject,
String username, char[] password, String realmName)
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, SecurityLoggerInfo.auditAtnRefusedError,
username);
}
if(getAuditManager().isAuditOn()){
getAuditManager().authentication(username, realmName, false);
}
if( e instanceof LoginException )
throw (LoginException)e;
else
throw (LoginException)
new LoginException("Login failed: " + e.getMessage()).initCause(e);
}
if(getAuditManager().isAuditOn()){
getAuditManager().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 Subject jmacLogin(Subject subject,
X500Principal x500Principal) throws LoginException {
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;
String jaasCtx = certRealm.getJAASContext();
if (jaasCtx != null) {
// The subject has the Cretificate Credential.
LoginContext lg = new LoginContext(jaasCtx, fs, dummyCallback);
lg.login();
}
certRealm.authenticate(fs, x500Name);
} catch(Exception ex) {
if (_logger.isLoggable(Level.INFO)) {
_logger.log(Level.INFO, SecurityLoggerInfo.auditAtnRefusedError,
userName);
}
if (getAuditManager().isAuditOn()){
getAuditManager().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 (getAuditManager().isAuditOn()){
getAuditManager().authentication(userName,
CertificateRealm.AUTH_TYPE, true);
}
// do not set the security Context
return subject;
}
public static Subject jmacLogin(Subject subject, String identityAssertion, String realm) throws LoginException {
if (subject == null) {
subject = new Subject();
}
final Subject fs = subject;
String userName = identityAssertion;
try {
if (realm == null || "".equals(realm)) {
realm = Realm.getDefaultRealm();
}
Realm realmInst = Realm.getInstance(realm);
final Enumeration groups = realmInst.getGroupNames(userName);
if (groups != null && groups.hasMoreElements()) {
AppservAccessController.doPrivileged(new PrivilegedAction() {
public java.lang.Object run() {
while (groups.hasMoreElements()) {
String grp = (String) groups.nextElement();
fs.getPrincipals().add(new Group(grp));
}
return fs;
}
});
}
} catch (Exception ex) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "Exception when trying to populate groups for CallerPrincipal " + identityAssertion, ex);
}
}
return subject;
}
/**
* A special case login for handling X509CertificateCredential.
* This does not get triggered based on current RI code. See X500Login.
*
*/
private static void doCertificateLogin(Subject s)
throws LoginException
{
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(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm, true);
}
} catch(LoginException le){
if(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm, false);
}
throw le;
}
}
/**
* A special case login for anonymous credentials (no login info).
*
*/
private static void doAnonLogin()
throws LoginException
{
// instance of anononymous credential login with guest
SecurityContext.setUnauthenticatedContext();
if(_logger.isLoggable(Level.FINE)){
_logger.log(Level.FINE,"Set anonymous security context.");
}
}
/**
* A special case login for GSSUPName credentials.
*
*/
private static void doGSSUPLogin(Subject s)
throws LoginException
{
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(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm, true);
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("GSSUP login succeeded for : " + user);
}
} catch (LoginException le){
if(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm, false);
}
throw le;
}
}
/**
* 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.
*
*/
public static void doX500Login(Subject s, String appModuleID)
throws LoginException
{
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;
String jaasCtx = certRealm.getJAASContext();
if (jaasCtx != null) {
// The subject has the Cretificate Credential.
LoginContext lg = new LoginContext(jaasCtx, s, new ServerLoginCallbackHandler(user, null, appModuleID));
lg.login();
}
certRealm.authenticate(s, x500name);
realm_name = CertificateRealm.AUTH_TYPE;
if(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm_name, true);
}
} else {
_logger.warning(SecurityLoggerInfo.certLoginBadRealmError);
realm_name = realm.getName();
setSecurityContext(user, s, realm_name);
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine("X.500 name login succeeded for : " + user);
}
} catch (LoginException le){
if(getAuditManager().isAuditOn()){
getAuditManager().authentication(user, realm_name, false);
}
throw le;
} catch (Exception ex) {
throw (LoginException)new LoginException(ex.toString()).initCause(ex);
}
}
/**
* Retrieve a public credential of the given type (java class) from the
* subject.
*
* <P>This method retains the RI assumption that only the first
* credential of the given type is used.
*
*/
private static Object getPublicCredentials(Subject s, Class<?> cls)
throws LoginException
{
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.getMessage()).initCause(e);
}
return obj;
}
/**
* Retrieve a private credential of the given type (java class) from the
* subject.
*
* <P>This method retains the RI assumption that only the first
* credential of the given type is used.
*
*/
private static Object getPrivateCredentials(Subject subject,
Class<?> cls)
throws LoginException
{
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.getMessage()).initCause(e);
}
return obj;
}
/**
* 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
*/
private static void setSecurityContext(String userName,
Subject subject, String realm) {
SecurityContext securityContext = new SecurityContext(userName, subject, realm);
SecurityContext.setCurrent(securityContext);
}
/**
* Set the current security context on the Thread Local Storage to null.
*
*/
private static void unsetSecurityContext() {
SecurityContext.setCurrent((SecurityContext)null);
}
/**
* 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 <i> username_password</i> or
* <i> certificate </i> based login.
* @param CallbackHandler the callback handler to gather user information.
* @exception LoginException the exception thrown by the callback handler.
*/
public static Subject doClientLogin(int type,
javax.security.auth.callback.CallbackHandler jaasHandler)
throws LoginException
{
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 == SecurityConstants.USERNAME_PASSWORD ){
AppservAccessController.doPrivileged(new PrivilegedAction() {
public java.lang.Object run() {
try{
LoginContext lg =
new LoginContext(SecurityConstants.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 == SecurityConstants.CERTIFICATE){
AppservAccessController.doPrivileged(new PrivilegedAction() {
public java.lang.Object run() {
try{
LoginContext lg =
new LoginContext(SecurityConstants.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 == SecurityConstants.ALL){
AppservAccessController.doPrivileged(new PrivilegedAction() {
public java.lang.Object run() {
try{
LoginContext lgup =
new LoginContext(SecurityConstants.CLIENT_JAAS_PASSWORD,
subject, handler);
LoginContext lgc =
new LoginContext(SecurityConstants.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(SecurityConstants.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;
}
}
/**
* Perform logout on the client side.
* @exception LoginException
*/
public static void doClientLogout() throws LoginException {
unsetClientSecurityContext();
}
/**
* Performs Digest authentication based on RFC 2617. It
*
* @param digestCred DigestCredentials
*/
public static void login(DigestCredentials digestCred) throws javax.security.auth.login.LoginException {
Subject subject = new Subject();
subject.getPrivateCredentials().add(digestCred);
String jaasCtx = null;
try {
jaasCtx = Realm.getInstance(digestCred.getRealmName()).getJAASContext();
} catch (Exception ex) {
if (ex instanceof LoginException) {
throw (LoginException) ex;
} else {
throw (LoginException) new LoginException(ex.toString()).initCause(ex);
}
}
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, subject, dummyCallback);
lg.login();
} catch (Exception e) {
if (_logger.isLoggable(Level.INFO)) {
_logger.log(Level.INFO, SecurityLoggerInfo.auditAtnRefusedError, digestCred.getUserName());
}
if (_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.FINEST, "doPasswordLogin fails", e);
}
if (getAuditManager().isAuditOn()) {
getAuditManager().authentication(digestCred.getUserName(), digestCred.getRealmName(), false);
}
if (e instanceof LoginException) {
throw (LoginException) e;
} else {
throw (LoginException) new LoginException("Login failed: " + e.getMessage()).initCause(e);
}
}
setSecurityContext(digestCred.getUserName(), subject, digestCred.getRealmName());
}
/**
* 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
*
*/
private static void postClientAuth(Subject subject, Class<?> clazz){
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,
SecurityLoggerInfo.securityAccessControllerActionError,
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;
}
}
}
/**
* 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
*/
private static void setClientSecurityContext(String username,
Subject subject) {
ClientSecurityContext securityContext =
new ClientSecurityContext(username, subject);
ClientSecurityContext.setCurrent(securityContext);
}
/**
* Unsets the current appclient security context on the Thread
* Local Storage
*/
private static void unsetClientSecurityContext() {
ClientSecurityContext.setCurrent(null);
}
}