package org.ovirt.engine.core.utils.kerberos; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import org.apache.log4j.Logger; import org.ovirt.engine.core.dns.DnsSRVLocator.DnsSRVResult; import org.ovirt.engine.core.utils.CLIParser; /** * A tool to create a krb5.conf file from a template using the supplied domain list. For each domain an SRV DNS request * will be made to extract the relevant KDC's. The (configurable) output file is a valid krb5.conf to by the ENGINE * server. */ public class KrbConfCreator { protected static final String REALMS_SECTION = "#realms"; protected static final String DOMAIN_REALM_SECTION = "#domain_realm"; public static final String DEFAULT_TKT_ENCTYPES_ARCFOUR_HMAC_MD5 = "default_tkt_enctypes = arcfour-hmac-md5"; private static final String DEFAULT_REALM = "default_realm"; public final static String seperator = System.getProperty("line.separator"); protected InputStream sourceFile; private List<String> realms; private CLIParser cliParser; private final String usage = " Usage: \n\t-domains=<comma seperated list>\n\t-destinationFile<file>" + "\n\t-m mixed mode. Will add a flag to support AD in 2003/2008 mixed mode will be added"; private final static Logger log = Logger.getLogger(KrbConfCreator.class); public KrbConfCreator(String... args) throws Exception { parseOptions(args); loadSourceFile(); extractRealmsFromDomains(); } public KrbConfCreator(String domains) throws Exception { loadSourceFile(); extractRealmsFromDomains(domains); } /** * @param args * arguments will be parsed as follow -domains="domainA,domainB," domains list * -destinationFile="path/to/destination/file -e encryption mode. When exist a flag to support AD in * 2003/2008 mixed mode * @return boolean true if to continue to next stage */ private void parseOptions(String[] args) { cliParser = new CLIParser(args); if (!cliParser.hasArg(Arguments.domains.name()) || !cliParser.hasArg(Arguments.krb5_conf_path.name())) { System.out.println("Missing arguments\nusage: " + usage); System.exit(1); } } private void loadSourceFile() throws FileNotFoundException { String template = "krb5.conf.template"; sourceFile = KrbConfCreator.class.getClassLoader().getResourceAsStream(template); if (sourceFile == null) { throw new FileNotFoundException(template + " was not found"); } log.debug("loaded template kr5.conf file " + template); } public StringBuffer parse() throws AuthenticationException { String mixedMode = "no"; if (cliParser.hasArg(Arguments.mixed_mode.name())) { mixedMode = cliParser.getArg(Arguments.mixed_mode.name()); } return parse(mixedMode); } public StringBuffer parse(String mixedMode) throws AuthenticationException { StringBuffer sb = new StringBuffer(); Scanner scanner = new Scanner(sourceFile); while (scanner.hasNextLine()) { String line = scanner.nextLine(); // set the first domain in the list to be the default REALM if (line.matches("#" + DEFAULT_REALM + ".*")) { line = DEFAULT_REALM + " = " + realms.get(0); } // Active directory in 2003 mode hack + IPA if (line.matches(".*" + DEFAULT_TKT_ENCTYPES_ARCFOUR_HMAC_MD5)) { if (mixedMode != null && (mixedMode.equalsIgnoreCase("y") || mixedMode.equalsIgnoreCase("yes"))) { line = line.replace("#", ""); log.debug("setting default_tkt_enctypes "); } } // populate realms by domains if (line.matches(REALMS_SECTION)) { line = appendRealms(realms); log.debug("setting realms"); } // populate domain realms by domains if (line.matches(DOMAIN_REALM_SECTION)) { line = appendDomainRealms(realms); log.debug("setting domain realm"); } sb.append(line + seperator); } return sb; } private void extractRealmsFromDomains() { String domains = cliParser.getArg(Arguments.domains.name()); extractRealmsFromDomains(domains); String[] realmArray = domains.split(",", -1); List<String> realms = new ArrayList<String>(); for (String realm : realmArray) { realms.add(realm.toUpperCase()); } this.realms = realms; } private void extractRealmsFromDomains(String domains) { String[] realmArray = domains.split(",", -1); List<String> realms = new ArrayList<String>(); for (String realm : realmArray) { realms.add(realm.toUpperCase().trim()); } this.realms = realms; } public void toFile(StringBuffer sb) throws FileNotFoundException, IOException { File outputConfig; if (cliParser.hasArg(Arguments.krb5_conf_path.name())) { outputConfig = new File(cliParser.getArg(Arguments.krb5_conf_path.name())); } else { outputConfig = new File(System.getProperty("java.io.tmpdir") + File.separator + InstallerConstants.KRB_FILE_NAME); } toFile(outputConfig.getAbsolutePath(), sb); } public void toFile(String krb5ConfPath, StringBuffer sb) throws FileNotFoundException, IOException { FileOutputStream fos = new FileOutputStream(krb5ConfPath); fos.write(sb.toString().getBytes()); fos.close(); } private String appendRealms(List<String> realms) throws AuthenticationException { StringBuffer text = new StringBuffer(" [realms]"); KDCLocator locator = new KDCLocator(); for (String realm : realms) { DnsSRVResult kdc; try { kdc = locator.getKdc(KDCLocator.TCP, realm); String[] addresses = kdc.getAddresses(); if (addresses.length == 0) { // kdc not found for this realm throw new IllegalArgumentException(InstallerConstants.ERROR_PREFIX + " there are no KDCs for for realm " + realm + ". Realm name may not be valid"); } text.append(seperator + "\t" + realm + " = {" + seperator); // output REALM = { for (String address : addresses) { text.append("\t\tkdc = " + address + seperator); // output kdc = address } text.append("\t}" + seperator); // append line} } catch (Exception ex) { AuthenticationResult result = KerberosUtils.convertDNSException(ex); System.out.println(InstallerConstants.ERROR_PREFIX + result.getDetailedMessage() + "." + getProblematicRealmExceptionMsg(realm)); throw new AuthenticationException(result); } } return text.toString(); } // The domain realm section is the following section // [domain_realm] // .example.com = EXAMPLE.COM // .second.example.com = SECOND.EXAMPLE.COM private String appendDomainRealms(List<String> realms) throws AuthenticationException { StringBuffer text = new StringBuffer(" [domain_realm]\n"); for (String realm : realms) { text.append("\t" + realm.toLowerCase() + " = " + realm.toUpperCase() + "\n"); } return text.toString(); } private String getProblematicRealmExceptionMsg(String realm) { return (realm != null) ? " Problematic domain is: " + realm.toLowerCase() : ""; } public static void main(String[] args) throws FileNotFoundException { try { KrbConfCreator kerbParser = new KrbConfCreator(args); StringBuffer buffer = kerbParser.parse(); kerbParser.toFile(buffer); } catch (Exception e) { System.out.println("Error: " + e.getMessage()); if (e instanceof AuthenticationException) { System.exit(((AuthenticationException) e).getAuthResult().getExitCode()); } System.exit(1); } } private enum Arguments { domains, krb5_conf_path, mixed_mode } }