/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package com.amazon.dlic.auth.http.kerberos.util; //Source: Apache Kerby project //https://directory.apache.org/kerby/ import java.io.IOException; import java.nio.file.Path; import java.security.Principal; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; /** * JAAS utilities for Kerberos login. */ public final class JaasKrbUtil { private static boolean debug = false; private JaasKrbUtil() {} public static void setDebug(final boolean debug) { JaasKrbUtil.debug = debug; } public static Subject loginUsingPassword(final String principal, final String password) throws LoginException { final Set principals = new HashSet(); principals.add(new KerberosPrincipal(principal)); final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); final Configuration conf = usePassword(principal); final String confName = "PasswordConf"; final CallbackHandler callback = new KrbCallbackHandler(principal, password); final LoginContext loginContext = new LoginContext(confName, subject, callback, conf); loginContext.login(); return loginContext.getSubject(); } public static Subject loginUsingTicketCache(final String principal, final Path cachePath) throws LoginException { final Set principals = new HashSet(); principals.add(new KerberosPrincipal(principal)); final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); final Configuration conf = useTicketCache(principal, cachePath); final String confName = "TicketCacheConf"; final LoginContext loginContext = new LoginContext(confName, subject, null, conf); loginContext.login(); return loginContext.getSubject(); } public static Subject loginUsingKeytab(final Set principalAsStrings, final Path keytabPath, final boolean initiator) throws LoginException { final Set principals = new HashSet(); for (String p : principalAsStrings) { principals.add(new KerberosPrincipal(p)); } final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); final Configuration conf = useKeytab("*", keytabPath, initiator); final String confName = "KeytabConf"; final LoginContext loginContext = new LoginContext(confName, subject, null, conf); loginContext.login(); return loginContext.getSubject(); } public static Configuration usePassword(final String principal) { return new PasswordJaasConf(principal); } public static Configuration useTicketCache(final String principal, final Path credentialPath) { return new TicketCacheJaasConf(principal, credentialPath); } public static Configuration useKeytab(final String principal, final Path keytabPath, final boolean initiator) { return new KeytabJaasConf(principal, keytabPath, initiator); } private static String getKrb5LoginModuleName() { return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.auth.module.Krb5LoginModule" : "com.sun.security.auth.module.Krb5LoginModule"; } static class KeytabJaasConf extends Configuration { private final String principal; private final Path keytabPath; private final boolean initiator; public KeytabJaasConf(final String principal, final Path keytab, final boolean initiator) { this.principal = principal; this.keytabPath = keytab; this.initiator = initiator; } @Override public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { final Map options = new HashMap(); options.put("keyTab", keytabPath.toAbsolutePath().toString()); options.put("principal", principal); options.put("useKeyTab", "true"); options.put("storeKey", "true"); options.put("doNotPrompt", "true"); options.put("renewTGT", "false"); options.put("refreshKrb5Config", "true"); options.put("isInitiator", String.valueOf(initiator)); options.put("debug", String.valueOf(debug)); return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; } } static class TicketCacheJaasConf extends Configuration { private final String principal; private final Path clientCredentialPath; public TicketCacheJaasConf(final String principal, final Path clientCredentialPath) { this.principal = principal; this.clientCredentialPath = clientCredentialPath; } @Override public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { final Map options = new HashMap(); options.put("principal", principal); options.put("storeKey", "false"); options.put("doNotPrompt", "false"); options.put("useTicketCache", "true"); options.put("renewTGT", "true"); options.put("refreshKrb5Config", "true"); options.put("isInitiator", "true"); options.put("ticketCache", clientCredentialPath.toAbsolutePath().toString()); options.put("debug", String.valueOf(debug)); return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; } } static class PasswordJaasConf extends Configuration { private final String principal; public PasswordJaasConf(final String principal) { this.principal = principal; } @Override public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { final Map options = new HashMap<>(); options.put("principal", principal); options.put("storeKey", "true"); options.put("useTicketCache", "true"); options.put("useKeyTab", "false"); options.put("renewTGT", "true"); options.put("refreshKrb5Config", "true"); options.put("isInitiator", "true"); options.put("debug", String.valueOf(debug)); return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; } } public static class KrbCallbackHandler implements CallbackHandler { private final String principal; private final String password; public KrbCallbackHandler(final String principal, final String password) { this.principal = principal; this.password = password; } @Override public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof PasswordCallback) { final PasswordCallback pc = (PasswordCallback) callbacks[i]; if (pc.getPrompt().contains(principal)) { pc.setPassword(password.toCharArray()); break; } } } } } }