/**
*
*/
package org.ovirt.engine.core.bll.adbroker;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.LogCompat;
import org.ovirt.engine.core.compat.LogFactoryCompat;
import org.ovirt.engine.core.utils.kerberos.AuthenticationResult;
import org.ovirt.engine.core.utils.kerberos.KerberosReturnCodeParser;
/**
*
* Implements a GSSAPI directory context authentication strategy to be used with
* LDAP
*
*
*/
public class GSSAPIDirContextAuthenticationStrategy implements DirContextAuthenticationStrategy {
private static final String GSS_API_AUTHENTICATION = "GSSAPI";
private static final String LOGIN_MODULE_POLICY_NAME = "RHEVKerberosAuth";
private static LogCompat log = LogFactoryCompat.getLog(GSSAPIDirContextAuthenticationStrategy.class);
private LoginContext loginContext;
private String password;
private String userName;
private String realm;
private boolean explicitAuth;
public void setExplicitAuth(boolean explicitAuth) {
this.explicitAuth = explicitAuth;
}
public LoginContext getLoginContext() {
return loginContext;
}
/**
* @param password
* @param userName
* @param explicitAuth
*
*/
public GSSAPIDirContextAuthenticationStrategy(String userName, String password, String realm, boolean explicitAuth) {
this.userName = userName;
this.password = password;
this.realm = realm;
this.explicitAuth = explicitAuth;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.ldap.core.support.DirContextAuthenticationStrategy
* #setupEnvironment(java.util.Hashtable, java.lang.String,
* java.lang.String)
*/
@Override
public void setupEnvironment(Hashtable env, String userDn, String password) throws NamingException {
env.put(Context.SECURITY_AUTHENTICATION, GSS_API_AUTHENTICATION);
String qopValue = Config.<String>GetValue(ConfigValues.SASL_QOP);
env.put("javax.security.sasl.qop", qopValue);
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.ldap.core.support.DirContextAuthenticationStrategy
* #processContextAfterCreation(javax.naming.directory.DirContext,
* java.lang.String, java.lang.String)
*/
@Override
public DirContext processContextAfterCreation(DirContext ctx, String userDn, String password)
throws NamingException {
return ctx;
}
public void authenticate() throws EngineDirectoryServiceException {
UsersDomainsCacheManager usersDomainsCacheManager = UsersDomainsCacheManagerService.getInstance();
UserDomainInfo userDomainInfo = usersDomainsCacheManager.associateUserWithDomain(this.userName,
this.realm.toLowerCase());
loginContext = null;
synchronized (userDomainInfo) {
// In case authentication is performed in an implicit way (as a
// result of internal command) try and get
// login context from cache
if (!explicitAuth) {
loginContext = userDomainInfo.getLoginContext();
}
if (!validLoginContext()) {
explicitAuth(userDomainInfo);
}
}
}
private void explicitAuth(UserDomainInfo userDomainInfo) throws EngineDirectoryServiceException {
GSSAPICallbackHandler callbackHandler = new GSSAPICallbackHandler(userName, password);
authenticateToKDC(callbackHandler, userDomainInfo);
}
private void authenticateToKDC(GSSAPICallbackHandler callbackHandler, UserDomainInfo userDomainInfo) throws EngineDirectoryServiceException {
try {
loginContext = new LoginContext(LOGIN_MODULE_POLICY_NAME, callbackHandler);
loginContext.login();
userDomainInfo.setLoginContext(loginContext);
if (log.isDebugEnabled()) {
log.debug("Successful login for user " + userName);
}
} catch (LoginException ex) {
// JAAS throws login exception due to various reasons.
// We check if the login exception matches a case where the user
// provided wrong authentication details, or
// if there was another error - in case the user provided wrong
// authentication details, we will abort the kdc search
loginContext = null;
KerberosReturnCodeParser parser = new KerberosReturnCodeParser();
AuthenticationResult result = parser.parse(ex.getMessage());
if (result == AuthenticationResult.OTHER || result == null) {
// An error our error parser does not recognize
log.error("Error from Kerberos: " + ex.getMessage());
} else {
StringBuilder error = new StringBuilder();
error.append(result.getDetailedMessage());
log.error(error.toString());
}
throw new EngineDirectoryServiceException(result);
}
}
private boolean validLoginContext() {
if (loginContext == null)
return false;
Subject subject = loginContext.getSubject();
if (subject == null)
return false;
Set<KerberosTicket> privateCreds = subject.getPrivateCredentials(KerberosTicket.class);
if (privateCreds == null || privateCreds.size() == 0)
return false;
Iterator<KerberosTicket> iterator = privateCreds.iterator();
KerberosTicket ticket = iterator.next();
return ticket.isCurrent();
}
}