package org.ovirt.engine.core.utils.kerberos;
import static org.ovirt.engine.core.utils.kerberos.InstallerConstants.ERROR_PREFIX;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.log4j.Logger;
import org.ovirt.engine.core.utils.CLIParser;
/**
* Utility to verify Kerberos installation
*
*/
public class KerberosConfigCheck {
private LoginContext lc;
private final static Logger log = Logger.getLogger(KerberosConfigCheck.class);
public enum Arguments {
domains,
user,
password,
jaas_file,
jboss_dir,
krb5_conf_path;
}
// This function gets the username and adjusts it doing the following:
// 1. If the username contains @, for example:
// user@domain, it returns user@DOMAIN
// 2. If the username doesn't contain @ it returns the input user name
// 3. For inputs like "@", "user@" and @domain it just returns the input
private static String adjustUserName(String userName) {
String returnUserName = userName;
if (userName.contains("@")) {
String[] parts = userName.split("@");
int numberOfParts = parts.length;
switch (numberOfParts) {
case 1:
returnUserName = parts[0];
break;
case 2:
returnUserName = parts[0] + '@' + parts[1].toUpperCase();
break;
default:
returnUserName = userName;
break;
}
}
return returnUserName;
}
/**
* JAAS callback handler. JAAS uses this class during login - it provides an array of callbacks (including the
* NameCallback and PasswordCallback) It is the responsibility of the implementor of CallbackHandler to set the user
* name and the password on the relevant call backs.
*/
private static class KerberosUtilCallbackHandler implements CallbackHandler {
private String username;
private String password;
public KerberosUtilCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback cb = (NameCallback) callbacks[i];
cb.setName(username);
} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback) callbacks[i];
cb.setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
}
private void printUsage() {
System.out.println("Usage:");
System.out
.println("KerberosConfigCheck: -domains=<domains> -user=<user> -password=<password> -jaas_conf=<jaas conf path> krb5_conf_path=<krb5 conf path>");
}
private boolean validate(CLIParser parser) {
Arguments[] argsToValidate =
{ Arguments.domains, Arguments.user, Arguments.password, Arguments.jaas_file, Arguments.krb5_conf_path };
for (Arguments argument : argsToValidate) {
if (!parser.hasArg(argument.name())) {
System.out.println(argument.name() + " is required");
return false;
}
}
return true;
}
public static void main(String[] args) {
KerberosConfigCheck util = new KerberosConfigCheck();
CLIParser parser = new CLIParser(args);
if (!util.validate(parser)) {
util.printUsage();
System.exit(1);
}
String username = adjustUserName(parser.getArg(Arguments.user.name()));
String password = parser.getArg(Arguments.password.name());
String jaasFile = parser.getArg(Arguments.jaas_file.name());
String krb5ConfFile = parser.getArg(Arguments.krb5_conf_path.name());
String domains = parser.getArg(Arguments.domains.name());
StringBuffer userGuid = new StringBuffer();
try {
util.checkInstallation(domains, username, password, jaasFile, krb5ConfFile, userGuid);
} catch (AuthenticationException e) {
System.err.println(ERROR_PREFIX + e.getMessage());
System.exit(e.getAuthResult().getExitCode());
}
}
public void checkInstallation(String domains,
String username,
String password,
String jaasFile,
String krb5ConfFile,
StringBuffer userGuid)
throws AuthenticationException {
String[] domainsList = domains.split(",", -1);
String domain = domainsList[0].trim();
String realm = domain.toUpperCase();
validateKerberosInstallation(realm, username, password, jaasFile, krb5ConfFile, userGuid);
}
public void validateKerberosInstallation(String realm,
String username,
String password,
String pathToJAASFile,
String pathToKrb5ConfFile,
StringBuffer userGuid) throws AuthenticationException {
AuthenticationResult authResult = authenticate(realm, username, password, pathToJAASFile, pathToKrb5ConfFile);
if (authResult == AuthenticationResult.OK) {
// Successful authentication was acehived, no point in searching for
// KDcs that use UDP
AuthenticationResult actionResult = promptSuccessfulAuthentication(realm, username, userGuid);
if (actionResult != AuthenticationResult.OK) {
throw new AuthenticationException(actionResult);
}
return;
} else {
throw new AuthenticationException(authResult);
}
}
private AuthenticationResult promptSuccessfulAuthentication(String realm, String username, StringBuffer userGuid) {
AuthenticationResult authResult = AuthenticationResult.OTHER;
try {
// Executing the code that will perform the LDAP query to get the
// user and print its GUID.
// A Windows domain is lowercase string of Keberos realm.
authResult =
(AuthenticationResult) Subject.doAs(lc.getSubject(), new JndiAction(username,
realm.toLowerCase(),
userGuid));
} finally {
if (lc != null) {
try {
lc.logout();
} catch (LoginException e) {
System.out.println(ERROR_PREFIX + " logout failed " + e.getMessage());
}
}
}
return authResult;
}
private AuthenticationResult authenticate(String realm, String username, String password,
String pathToJAASFile, String pathToKrb5File) {
// Set the realm to authenticate to and the path to the JAAS file that
// will define
// that JAAS is using kerberos login module
System.setProperty("java.security.krb5.conf", pathToKrb5File);
// Get kdcs for the relevant protocol (tcp or udp) and for the given
// realm
System.setProperty("java.security.auth.login.config", pathToJAASFile);
return checkAuthentication(username, password);
}
private AuthenticationResult checkAuthentication(String username, String password) {
AuthenticationResult result = AuthenticationResult.OK;
try {
lc = new LoginContext("KerberosUtil", new KerberosUtilCallbackHandler(username, password));
lc.login();
log.debug("Check authentication finished successfully ");
} catch (LoginException ex) {
String resultMessage = ex.getMessage();
KerberosReturnCodeParser parser = new KerberosReturnCodeParser();
result = parser.parse(resultMessage);
if (result != AuthenticationResult.OTHER) {
return result;
} else {
System.out.println(ERROR_PREFIX + " exception message: " + ex.getMessage());
}
}
return result;
}
}