package org.ovirt.engine.core.utils.kerberos;
import java.net.URI;
import java.security.PrivilegedAction;
import java.util.Hashtable;
import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.apache.log4j.Logger;
import org.ovirt.engine.core.dns.DnsSRVLocator.DnsSRVResult;
import org.ovirt.engine.core.ldap.LdapProviderType;
import org.ovirt.engine.core.ldap.LdapSRVLocator;
import org.ovirt.engine.core.ldap.RootDSEData;
import org.ovirt.engine.core.utils.ipa.RHDSUserContextMapper;
/**
* JAAS Privileged action to be run when KerbersUtil successfully authenticates. This action performs ldap query to
* retrieve information on the authenticated user and prints the object GUID of that user.
*/
public class JndiAction implements PrivilegedAction {
private String userName;
private String domainName;
private LdapProviderType ldapProviderType = LdapProviderType.activeDirectory;
private StringBuffer userGuid;
private final static Logger log = Logger.getLogger(JndiAction.class);
public JndiAction(String userName, String domainName, StringBuffer userGuid) {
this.userName = userName;
this.domainName = domainName;
this.ldapProviderType = LdapProviderType.activeDirectory;
this.userGuid = userGuid;
}
@Override
public Object run() {
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put("java.naming.ldap.attributes.binary", "objectGUID");
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
// Send an SRV record DNS query to retrieve all the LDAP servers in the domain
LdapSRVLocator locator = new LdapSRVLocator();
DnsSRVResult ldapDnsResult;
try {
ldapDnsResult = locator.getLdapServers(domainName);
} catch (Exception ex) {
return KerberosUtils.convertDNSException(ex);
}
DirContext ctx = null;
String currentLdapServer = null;
if (ldapDnsResult == null || ldapDnsResult.getNumOfValidAddresses() == 0) {
return AuthenticationResult.CANNOT_FIND_LDAP_SERVER_FOR_DOMAIN;
}
// Goes over all the retrieved LDAP servers
for (int counter = 0; counter < ldapDnsResult.getNumOfValidAddresses(); counter++) {
String address = ldapDnsResult.getAddresses()[counter];
try {
// Constructs an LDAP url in a format of ldap://hostname:port (based on the data in the SRV record
// This URL is not enough in order to query for user - as for querying users, we should also provide a
// base dn, for example: ldap://hostname:389/DC=abc,DC=com . However, this URL (ldap:hostname:port)
// suffices for
// getting the rootDSE information, which includes the baseDN.
URI uri = locator.constructURI("LDAP", address);
env.put(Context.PROVIDER_URL, uri.toString());
ctx = new InitialDirContext(env);
// Get the base DN from rootDSE
String domainDN = getDomainDN(ctx);
if (domainDN != null) {
// Append the base DN to the ldap URL in order to construct a full ldap URL (in form of
// ldap:hostname:port/baseDN ) to query for the user
StringBuilder ldapQueryPath = new StringBuilder(uri.toString());
ldapQueryPath.append("/").append(domainDN);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// Adding all the three attributes possible, as RHDS doesn't return the nsUniqueId by default
controls.setReturningAttributes(new String[]{"nsUniqueId", "ipaUniqueId","objectGuid"});
currentLdapServer = ldapQueryPath.toString();
env.put(Context.PROVIDER_URL, currentLdapServer);
// Run the LDAP query to get the user
ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> answer = executeQuery(ctx, controls, prepareQuery());
while (answer.hasMoreElements()) {
// Print the objectGUID for the user
userGuid.append(guidFromResults(answer.next()));
log.debug("User guid is: " + userGuid.toString());
return AuthenticationResult.OK;
}
System.out.println("No user in Directory was found for " + userName
+ ". Trying next LDAP server in list");
} else {
System.out.println(InstallerConstants.ERROR_PREFIX
+ " Failed to query rootDSE in order to get the baseDN. Could not query for user "
+ userName + " in domain" + domainName);
}
} catch (CommunicationException ex) {
System.out.println("Cannot connect to LDAP URL: " + currentLdapServer
+ ". Trying next LDAP server in list (if exists)");
} catch (AuthenticationException ex) {
ex.printStackTrace();
AuthenticationResult result = AuthenticationResult.OTHER;
KerberosReturnCodeParser parser = new KerberosReturnCodeParser();
result = parser.parse(ex.toString());
String errorMsg = result.getDetailedMessage().replace("Authentication Failed", "LDAP query Failed");
System.out.println(InstallerConstants.ERROR_PREFIX + errorMsg);
} catch (Exception ex) {
System.out.println("General error has occured" + ex.getMessage());
ex.printStackTrace();
break;
} finally {
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} // end of loop on addresses
return AuthenticationResult.NO_USER_INFORMATION_WAS_FOUND_FOR_USER;
}
private String guidFromResults(SearchResult sr) throws NamingException {
String guidString = "";
if (ldapProviderType.equals(LdapProviderType.ipa)) {
String ipaUniqueId = (String) sr.getAttributes().get("ipaUniqueId").get();
guidString += ipaUniqueId;
} else if (ldapProviderType.equals(LdapProviderType.rhds)) {
String nsUniqueId = (String) sr.getAttributes().get("nsUniqueId").get();
guidString += RHDSUserContextMapper.getGuidFromNsUniqueId(nsUniqueId);
} else {
Object objectGuid = sr.getAttributes().get("objectGUID").get();
byte[] guid = (byte[]) objectGuid;
guidString += ((new org.ovirt.engine.core.compat.Guid(guid, false)).toString());
}
return guidString;
}
private String prepareQuery() {
String query;
if (ldapProviderType.equals(LdapProviderType.ipa)) {
userName = userName.split("@")[0];
query = "(&(objectClass=posixAccount)(objectClass=krbPrincipalAux)(uid=" + userName + "))";
} else if (ldapProviderType.equals(LdapProviderType.rhds)) {
userName = userName.split("@")[0];
query = "(&(objectClass=person)(uid=" + userName + "))";
}
else {
StringBuilder queryBase = new StringBuilder("(&(sAMAccountType=805306368)(");
if (userName.contains("@")) {
queryBase.append("userPrincipalName=" + userName);
} else {
if (userName.length() > 20) {
queryBase.append("userPrincipalName=")
.append(userName)
.append("@")
.append(domainName.toUpperCase());
} else {
queryBase.append("sAMAccountName=").append(userName);
}
}
query = queryBase.append("))").toString();
}
return query;
}
private NamingEnumeration<SearchResult> executeQuery(DirContext ctx, SearchControls controls, String query)
throws NamingException {
NamingEnumeration<SearchResult> answer = ctx.search("", query, controls);
return answer;
}
private String getDomainDN(DirContext ctx) throws NamingException {
RootDSEData rootDSEData = new RootDSEData(ctx);
ldapProviderType = rootDSEData.getLdapProviderType();
return rootDSEData.getDomainDN();
}
}