/** * */ 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(); } }